From defa71bea192c7bffbe713edfc5dbe0b4f6b8edf Mon Sep 17 00:00:00 2001 From: fred Date: Mon, 24 Oct 2022 09:56:34 +0200 Subject: [PATCH] Release/v3.0.0 (#84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump Selenium.WebDriver.ChromeDriver (#74) Bumps [Selenium.WebDriver.ChromeDriver](https://github.com/jsakamoto/nupkg-selenium-webdriver-chromedriver) from 105.0.5195.5200 to 106.0.5249.6100. - [Release notes](https://github.com/jsakamoto/nupkg-selenium-webdriver-chromedriver/releases) - [Changelog](https://github.com/jsakamoto/nupkg-selenium-webdriver-chromedriver/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/jsakamoto/nupkg-selenium-webdriver-chromedriver/compare/v.105.0.5195.5200...v.106.0.5249.6100) --- updated-dependencies: - dependency-name: Selenium.WebDriver.ChromeDriver dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.2 (#65) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.2.0 to 17.3.2. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v17.2.0...v17.3.2) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: fred * Bump Selenium.WebDriver from 4.4.0 to 4.5.0 (#73) Bumps Selenium.WebDriver from 4.4.0 to 4.5.0. --- updated-dependencies: - dependency-name: Selenium.WebDriver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fixed CancellationTokenSource-timeout not aborting while-loop (#58) Co-authored-by: Charlie Stahlen * Include inner exception in the new exception message (#72) * Fixed formating * Bump actions/setup-dotnet from 2 to 3.0.0 (#75) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3.0.0. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3.0.0) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * moved sample proj * Bump actions/setup-dotnet from 3.0.0 to 3.0.1 (#76) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump Selenium.WebDriver from 4.5.0 to 4.5.1 (#79) Bumps Selenium.WebDriver from 4.5.0 to 4.5.1. --- updated-dependencies: - dependency-name: Selenium.WebDriver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * New actions (#81) * New actions * fixes in actions * fixed secret * added webhookuri to actions * Fixed appRoot in testbase * Feature/nshift (#50) * Added RowType to OrderRow * Added ShippingOption class and method to calculate orderrows for shipping on Cart * Added Shipping models * Refactoring of models * starting on delivery demo wseb * starting on delivery, some dock * Moved Calculate shipping order rows test * Fixed quantity * added some js * Added WebhookUri to MerchantSettings * Minor refactor and set WebhookUri * Changed weight to double * Added shippingCallbackResponse * Removed whitespaces * Added test for deserializing order data * Updated js for shippingHandler * Added method for tax calculation * order viewmodel changes * Update order after delivery selection. Merged cart and cartResponse * Made som classes internal * Deleted unused class * Made ctor public * Made fallback options a list * Added ui test for shipping option * Added VatPercent on products. added RowType * Updated shipping order test to use checkout api * Removed dotsettings.user. Changed som access modifiers * finishing delivery. Added shipping status/desc to order view * long for shippingFee in ShippingOption. Cleanup, tests * Added test cases as per Epic 8223 * Added titles on user stories for test cases * fixed issue with approot * UI test fix * added webhook uri * added webhook uri * Increased test stability for Leasing Payment test * Use a different org number depending on payment method * webhookuri in action * webhookuri in action * Testing without UI tests * readded ui tests * Improving test stability * Improve test stability for leasing Co-authored-by: Daniel Sundqvist Co-authored-by: Clement Joye * Updated readme * Rearranged params in MerchantSettings * Fixed typo in build file Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Charlie Stahlén <31145692+MankeyX@users.noreply.github.com> Co-authored-by: Charlie Stahlen Co-authored-by: Patric Forsgard Co-authored-by: Daniel Sundqvist Co-authored-by: Clement Joye --- .github/workflows/build_test_publish.yml | 291 -------------- .../deploy_pro_package_samplesite.yml | 37 ++ .github/workflows/dev.yml | 29 ++ .github/workflows/pro.yml | 26 ++ .github/workflows/template_build.yml | 92 +++++ .github/workflows/template_publish_webapp.yml | 53 +++ .github/workflows/template_version.yml | 29 ++ .github/workflows/test_deploy_samplesite.yml | 145 +++++++ .github/workflows/uat.yml | 27 ++ .gitignore | 1 + README.md | 31 +- .../Orders/Blocks/OrderBlock.cs | 6 + .../Payment/Blocks/AddShippingBlock.cs | 11 + .../Payment/Blocks/EditShippingBlock.cs | 32 ++ .../Payment/Blocks/SelectShippingBlock.cs | 53 +++ .../Payment/Items/ShippingOptionItem.cs | 10 + .../Payment/SveaPaymentFramePage.cs | 11 + .../PageObjectModels/Products/ProductsPage.cs | 3 + .../Sample.AspNetCore.SystemTests.csproj | 6 +- .../Services/TestDataService.cs | 15 +- .../Test/Base/TestBase.cs | 1 - .../Test/Helpers/IdentificationHelper.cs | 4 +- .../Test/Helpers/PaymentHelper.cs | 26 +- .../Test/Helpers/ShippingOptions.cs | 13 + .../Test/Helpers/SveaPaymentFrameHelper.cs | 166 +++++++- .../Test/PaymentTests/Base/PaymentTests.cs | 94 +++-- .../PaymentTests/Payment/LeasingOrderTests.cs | 2 +- .../Payment/ShippingOrderTests.cs | 369 ++++++++++++++++++ .../Controllers/CheckOutController.cs | 134 ++++--- .../Controllers/OrdersController.cs | 2 + .../Controllers/SveaController.cs | 47 ++- src/Samples/Sample.AspNetCore/Models/Cart.cs | 2 + .../Models/MerchantSettings.cs | 1 + src/Samples/Sample.AspNetCore/Models/Order.cs | 23 +- .../Models/ProductGenerator.cs | 6 +- .../Models/ViewModels/OrderViewModel.cs | 24 +- src/Samples/Sample.AspNetCore/Startup.cs | 17 +- .../Views/Orders/Details.cshtml | 23 +- .../Views/Products/Index.cshtml | 9 +- .../Components/CartSummary/Default.cshtml | 37 +- .../Sample.AspNetCore/appsettings.json | 3 +- src/Samples/Sample.AspNetCore/ngrok.yml | 6 +- .../Sample.AspNetCore/wwwroot/js/site.js | 41 +- .../App_Start/BundleConfig.cs | 0 .../App_Start/FilterConfig.cs | 0 .../App_Start/RouteConfig.cs | 0 .../Sample.AspNetFramework/Content/Site.css | 0 .../Content/bootstrap-theme.css | 0 .../Content/bootstrap-theme.css.map | 0 .../Content/bootstrap-theme.min.css | 0 .../Content/bootstrap-theme.min.css.map | 0 .../Content/bootstrap.css | 0 .../Content/bootstrap.css.map | 0 .../Content/bootstrap.min.css | 0 .../Content/bootstrap.min.css.map | 0 .../Controllers/HomeController.cs | 0 .../Sample.AspNetFramework/Global.asax | 0 .../Sample.AspNetFramework/Global.asax.cs | 0 .../Models/HomeViewModel.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Sample.AspNetFramework.csproj | 0 .../Sample.AspNetFramework.csproj.user | 0 .../Scripts/bootstrap.js | 0 .../Scripts/bootstrap.min.js | 0 .../Scripts/jquery-3.4.1.intellisense.js | 0 .../Scripts/jquery-3.4.1.js | 0 .../Scripts/jquery-3.4.1.min.js | 0 .../Scripts/jquery-3.4.1.min.map | 0 .../Scripts/jquery-3.4.1.slim.js | 0 .../Scripts/jquery-3.4.1.slim.min.js | 0 .../Scripts/jquery-3.4.1.slim.min.map | 0 .../Scripts/jquery.validate-vsdoc.js | 0 .../Scripts/jquery.validate.js | 0 .../Scripts/jquery.validate.min.js | 0 .../Scripts/jquery.validate.unobtrusive.js | 0 .../jquery.validate.unobtrusive.min.js | 0 .../Scripts/modernizr-2.8.3.js | 0 .../Views/Home/About.cshtml | 0 .../Views/Home/Contact.cshtml | 0 .../Views/Home/Index.cshtml | 0 .../Views/Shared/Error.cshtml | 0 .../Views/Shared/_Layout.cshtml | 0 .../Sample.AspNetFramework/Views/Web.config | 0 .../Views/_ViewStart.cshtml | 0 .../Sample.AspNetFramework/Web.Debug.config | 0 .../Sample.AspNetFramework/Web.Release.config | 0 .../Sample.AspNetFramework/Web.config | 0 .../Sample.AspNetFramework/favicon.ico | Bin .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../fonts/glyphicons-halflings-regular.woff2 | Bin .../Sample.AspNetFramework/packages.config | 0 src/Svea.WebPay.SDK.Tests/CheckoutTests.cs | 1 + .../Helpers/DataComparison.cs | 2 +- .../Helpers/DataSample.cs | 53 +++ .../Json/JsonConvertTests.cs | 12 +- .../Svea.WebPay.SDK.Tests.csproj | 2 +- src/Svea.WebPay.SDK.Tests/TestBase.cs | 11 +- .../UnitTests/CartTests.cs | 50 +++ .../UnitTests/OrderRowResponseTests.cs | 12 +- .../UnitTests/ShippingOptionTests.cs | 68 ++++ src/Svea.WebPay.SDK.sln | 9 +- src/Svea.WebPay.SDK/CheckoutApi/Addon.cs | 16 + src/Svea.WebPay.SDK/CheckoutApi/Cart.cs | 25 +- .../CheckoutApi/CartResponse.cs | 14 - .../CheckoutApi/CreateOrderModel.cs | 52 ++- src/Svea.WebPay.SDK/CheckoutApi/Data.cs | 12 +- .../CheckoutApi/FallbackOption.cs | 59 +++ src/Svea.WebPay.SDK/CheckoutApi/Field.cs | 14 + .../GetOrderShippingInformation.cs | 24 ++ .../CheckoutApi/IShippingOption.cs | 13 + src/Svea.WebPay.SDK/CheckoutApi/Location.cs | 16 + .../CheckoutApi/Merchantsettings.cs | 15 +- src/Svea.WebPay.SDK/CheckoutApi/OrderRow.cs | 144 +++++-- .../CheckoutApi/OrderRowResponse.cs | 91 ----- .../CheckoutValidationCallbackResponse.cs | 2 +- .../Response/ShippingCallbackResponse.cs | 20 + .../Response/ShippingDescriptionResponse.cs | 15 + .../Response/ShippingOptionResponse.cs | 14 + src/Svea.WebPay.SDK/CheckoutApi/RowType.cs | 8 + .../CheckoutApi/ShippingInformation.cs | 46 +++ .../CheckoutApi/ShippingOption.cs | 104 +++++ .../CheckoutApi/ShippingProvider.cs | 27 ++ .../CheckoutApi/UpdateOrderModel.cs | 11 +- .../Json/CustomMinorUnitConverter.cs | 1 - src/Svea.WebPay.SDK/OrderRowBase.cs | 11 + .../PaymentAdminApi/Models/OrderRow.cs | 1 + .../Response/AddOrderRowsResponse.cs | 2 +- .../Response/AddOrderRowsResponseObject.cs | 2 +- .../Response/CreditResponse.cs | 2 +- src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj | 6 +- src/Svea.WebPay.SDK/SveaHttpClient.cs | 12 +- 134 files changed, 2320 insertions(+), 637 deletions(-) delete mode 100644 .github/workflows/build_test_publish.yml create mode 100644 .github/workflows/deploy_pro_package_samplesite.yml create mode 100644 .github/workflows/dev.yml create mode 100644 .github/workflows/pro.yml create mode 100644 .github/workflows/template_build.yml create mode 100644 .github/workflows/template_publish_webapp.yml create mode 100644 .github/workflows/template_version.yml create mode 100644 .github/workflows/test_deploy_samplesite.yml create mode 100644 .github/workflows/uat.yml create mode 100644 src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/AddShippingBlock.cs create mode 100644 src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/EditShippingBlock.cs create mode 100644 src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/SelectShippingBlock.cs create mode 100644 src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Items/ShippingOptionItem.cs create mode 100644 src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/ShippingOptions.cs create mode 100644 src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/ShippingOrderTests.cs rename src/{ => Samples}/Sample.AspNetFramework/App_Start/BundleConfig.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/App_Start/FilterConfig.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/App_Start/RouteConfig.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/Site.css (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap-theme.css (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap-theme.css.map (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap-theme.min.css (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap-theme.min.css.map (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap.css (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap.css.map (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap.min.css (100%) rename src/{ => Samples}/Sample.AspNetFramework/Content/bootstrap.min.css.map (100%) rename src/{ => Samples}/Sample.AspNetFramework/Controllers/HomeController.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/Global.asax (100%) rename src/{ => Samples}/Sample.AspNetFramework/Global.asax.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/Models/HomeViewModel.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/Properties/AssemblyInfo.cs (100%) rename src/{ => Samples}/Sample.AspNetFramework/Sample.AspNetFramework.csproj (100%) rename src/{ => Samples}/Sample.AspNetFramework/Sample.AspNetFramework.csproj.user (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/bootstrap.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/bootstrap.min.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.intellisense.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.map (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.map (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery.validate-vsdoc.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery.validate.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery.validate.min.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.min.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Scripts/modernizr-2.8.3.js (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/Home/About.cshtml (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/Home/Contact.cshtml (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/Home/Index.cshtml (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/Shared/Error.cshtml (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/Shared/_Layout.cshtml (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/Web.config (100%) rename src/{ => Samples}/Sample.AspNetFramework/Views/_ViewStart.cshtml (100%) rename src/{ => Samples}/Sample.AspNetFramework/Web.Debug.config (100%) rename src/{ => Samples}/Sample.AspNetFramework/Web.Release.config (100%) rename src/{ => Samples}/Sample.AspNetFramework/Web.config (100%) rename src/{ => Samples}/Sample.AspNetFramework/favicon.ico (100%) rename src/{ => Samples}/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.eot (100%) rename src/{ => Samples}/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.svg (100%) rename src/{ => Samples}/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.ttf (100%) rename src/{ => Samples}/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff (100%) rename src/{ => Samples}/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff2 (100%) rename src/{ => Samples}/Sample.AspNetFramework/packages.config (100%) create mode 100644 src/Svea.WebPay.SDK.Tests/UnitTests/CartTests.cs create mode 100644 src/Svea.WebPay.SDK.Tests/UnitTests/ShippingOptionTests.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/Addon.cs delete mode 100644 src/Svea.WebPay.SDK/CheckoutApi/CartResponse.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/FallbackOption.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/Field.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/GetOrderShippingInformation.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/IShippingOption.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/Location.cs delete mode 100644 src/Svea.WebPay.SDK/CheckoutApi/OrderRowResponse.cs rename src/Svea.WebPay.SDK/CheckoutApi/{ => Response}/CheckoutValidationCallbackResponse.cs (89%) create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingCallbackResponse.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingDescriptionResponse.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingOptionResponse.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/RowType.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/ShippingInformation.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/ShippingOption.cs create mode 100644 src/Svea.WebPay.SDK/CheckoutApi/ShippingProvider.cs diff --git a/.github/workflows/build_test_publish.yml b/.github/workflows/build_test_publish.yml deleted file mode 100644 index 0b1dc2e8..00000000 --- a/.github/workflows/build_test_publish.yml +++ /dev/null @@ -1,291 +0,0 @@ -name: Build Test and Publish - -# Controls when the action will run. Triggers the workflow on push or pull request -on: - push: - branches: - - develop - - release/** - - hotfix/** - pull_request: - branches: - - develop - - master - release: - types: - - created - -env: - Configuration: Release - SlnPath: ./src/Svea.WebPay.SDK.sln - CsprojPath: ./src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj - dotnetVersion: | - 3.1.101 - 5.x - 6.x - AZURE_WEBAPP_PACKAGE_PATH: '.' - - -jobs: - - variables: - name: Create version number - runs-on: ubuntu-latest - outputs: - ENVIRONMENT: ${{ steps.setoutputvariables.outputs.ENVIRONMENT }} - AZURE_WEBAPP_NAME: ${{ steps.setoutputvariables.outputs.AZURE_WEBAPP_NAME }} - PUBLISH_PROFILE: ${{ steps.setoutputvariables.outputs.PUBLISH_PROFILE }} - NUGETVERSIONV2: ${{ steps.setoutputvariables.outputs.NUGETVERSIONV2 }} - - - steps: - - uses: actions/checkout@v3 - - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.13 - with: - versionSpec: '5.x.x' - - - name: Fetch all history for all tags and branches - run: git fetch --prune --unshallow - - - name: Use GitVersion - id: gitversion # step id used as reference for output values - uses: gittools/actions/gitversion/execute@v0.9.13 - - - name: Set dev environment variables - uses: allenevans/set-env@v2.2.0 - if: ${{ startsWith(github.ref, 'refs/heads/develop') }} || ${{ github.event_name == 'pull_request' }} - with: - ENVIRONMENT: dev - AZURE_WEBAPP_NAME: svea-webpay-sdk-001-dev - NUGETVERSIONV2: ${{ steps.gitversion.outputs.nuGetVersionV2 }} - - - name: Set uat environment variables - uses: allenevans/set-env@v2.2.0 - if: ${{ startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/heads/hotfix/') || contains(github.ref, '-beta')}} - with: - ENVIRONMENT: uat - AZURE_WEBAPP_NAME: svea-webpay-sdk-001-uat - NUGETVERSIONV2: ${{ steps.gitversion.outputs.nuGetVersionV2 }} - - - name: Set pro environment variables - uses: allenevans/set-env@v2.2.0 - if: ${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'refs/tags/*-*') }} - with: - ENVIRONMENT: pro - AZURE_WEBAPP_NAME: svea-webpay-sdk-001-pro - NUGETVERSIONV2: ${{ steps.gitversion.outputs.nuGetVersionV2 }} - - - id: setoutputvariables - name: Set output variables - run: | - echo "::set-output name=ENVIRONMENT::${ENVIRONMENT}" - echo "::set-output name=AZURE_WEBAPP_NAME::${AZURE_WEBAPP_NAME}" - echo "::set-output name=NUGETVERSIONV2::${NUGETVERSIONV2}" - - - build: - name: Build SDK ${{needs.variables.outputs.NUGETVERSIONV2}} ${{ matrix.os }} - needs: [variables] - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - - steps: - - - uses: actions/checkout@v3 - - - name: Setup .NET - uses: actions/setup-dotnet@v2 - with: - dotnet-version: ${{ env.dotnetVersion }} - - - name: Install dependencies - run: dotnet restore ${{ env.SlnPath }} - - - name: Build Solution - run: dotnet build ${{ env.SlnPath }} -p:Version=${{ needs.variables.outputs.NUGETVERSIONV2 }} --configuration ${{ env.Configuration }} --no-restore - - publish_test_nuget: - if: ${{ github.event_name != 'pull_request' && needs.variables.outputs.ENVIRONMENT == 'dev'}} - name: Build and publish alpha nuget - needs: [variables, build] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup .NET - uses: actions/setup-dotnet@v2 - with: - dotnet-version: ${{ env.dotnetVersion }} - source-url: https://nuget.pkg.github.com/sveawebpay/index.json - env: - NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - - - name: Install dependencies - run: dotnet restore ${{ env.SlnPath }} - - - name: Build Solution - run: dotnet build ${{ env.SlnPath }} -p:Version=${{ needs.variables.outputs.NUGETVERSIONV2 }} --configuration ${{ env.Configuration }} --no-restore - - - name: Nuget pack - run: dotnet pack ${{ env.CsprojPath }} -p:PackageVersion=${{ needs.variables.outputs.NUGETVERSIONV2 }} -p:Version=${{ needs.variables.outputs.NUGETVERSIONV2 }} --configuration=${{ env.Configuration }} --output=artifacts - - - name: Publish test package to GitHub Package Registry - run: dotnet nuget push "artifacts/*.nupkg" --api-key ${{secrets.GITHUB_TOKEN}} --source https://nuget.pkg.github.com/sveawebpay/index.json --skip-duplicate - - test: - name: Run Unit and UI tests - needs: [variables, build] - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v3 - - - name: Variable Substitution - uses: microsoft/variable-substitution@v1 - with: - # comma separated list of XML/JSON/YAML files in which tokens are to be substituted. Files names must be specified relative to the folder-path. - files: 'src/Samples/Sample.AspNetCore/appsettings.json, src/Samples/Sample.AspNetCore/ngrok.yml, src/Samples/Sample.AspNetCore.SystemTests/appsettings.json' - env: - authtoken: ${{ secrets.NGROK_AUTHTOKEN }} - Credentials.0.MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID }} - Credentials.0.Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} - Credentials.1.MerchantId: ${{ secrets.NORWAY_MERCHANT_ID }} - Credentials.1.Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} - Credentials.2.MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} - Credentials.2.Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} - MerchantSettings.PushUri: https://svea-webpay-sdk-001-${{ needs.variables.outputs.ENVIRONMENT }}.azurewebsites.net/api/svea/push/{checkout.order.uri}/?marketId={marketId} - MerchantSettings.TermsUri: https://svea-webpay-sdk-001-${{ needs.variables.outputs.ENVIRONMENT }}.azurewebsites.net/terms - MerchantSettings.CheckoutUri: https://svea-webpay-sdk-001-${{ needs.variables.outputs.ENVIRONMENT }}.azurewebsites.net/CheckOut/LoadPaymentMenu - MerchantSettings.ConfirmationUri: https://svea-webpay-sdk-001-${{ needs.variables.outputs.ENVIRONMENT }}.azurewebsites.net/checkout/thankyou - MerchantSettings.CheckoutValidationCallbackUri: https://svea-webpay-sdk-001-${{ needs.variables.outputs.ENVIRONMENT }}.azurewebsites.net/api/svea/validation/{checkout.order.uri}/?marketId={marketId} - - - name: Setup .NET - uses: actions/setup-dotnet@v2 - with: - dotnet-version: ${{ env.dotnetVersion }} - - - name: Install dependencies - run: dotnet restore ${{ env.SlnPath }} - - - name: Run Unit Tests - run: dotnet test --configuration ${{ env.Configuration }} src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj - env: - Credentials:0:MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID}} - Credentials:0:Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} - Credentials:1:MerchantId: ${{ secrets.NORWAY_MERCHANT_ID}} - Credentials:1:Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} - Credentials:2:MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} - Credentials:2:Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} - - - name: Publish Sdk Sample site - run: dotnet publish --configuration ${{ env.Configuration }} -p:Version=${{ needs.variables.outputs.nuGetVersionV2 }} --no-restore src/Samples/Sample.AspNetCore/Sample.AspNetCore.csproj --output '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/samplesite' - - - name: Setup ngrok - if: ${{ github.event_name != 'pull_request' }} - run: | - wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip - jar xvf ngrok-stable-linux-amd64.zip - chmod +x ngrok - - - name: Start ngrok - if: ${{ github.event_name != 'pull_request' }} - run: ./ngrok start -config ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/samplesite/ngrok.yml uitest${{ needs.variables.outputs.ENVIRONMENT }} & - - - name: Start SDK Sample project - if: ${{ github.event_name != 'pull_request' }} - working-directory: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/samplesite - run: - dotnet Sample.AspNetCore.dll & - env: - Credentials:0:MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID}} - Credentials:0:Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} - Credentials:1:MerchantId: ${{ secrets.NORWAY_MERCHANT_ID}} - Credentials:1:Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} - Credentials:2:MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} - Credentials:2:Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} - MerchantSettings:PushUri: https://uitest-${{ needs.variables.outputs.ENVIRONMENT }}-svea-webpay-sdk.eu.ngrok.io/api/svea/push/{checkout.order.uri}/?marketId={marketId} - MerchantSettings:TermsUri: https://uitest-${{ needs.variables.outputs.ENVIRONMENT }}-svea-webpay-sdk.eu.ngrok.io/terms - MerchantSettings:CheckoutUri: https://uitest-${{ needs.variables.outputs.ENVIRONMENT }}-svea-webpay-sdk.eu.ngrok.io/CheckOut/LoadPaymentMenu - MerchantSettings:ConfirmationUri: https://uitest-${{ needs.variables.outputs.ENVIRONMENT }}-svea-webpay-sdk.eu.ngrok.io/checkout/thankyou - MerchantSettings:CheckoutValidationCallbackUri: https://uitest-${{ needs.variables.outputs.ENVIRONMENT }}-svea-webpay-sdk.eu.ngrok.io/api/svea/validation/{checkout.order.uri}/?marketId={marketId} - - - - name: Verify chrome driver - shell: pwsh - run: | - ./src/Samples/Sample.AspNetCore.SystemTests/chrome-driver-udpate.ps1 - - - name: Run SDK UI tests - if: ${{ github.event_name != 'pull_request' && needs.variables.outputs.ENVIRONMENT != 'dev'}} - run: dotnet test --configuration ${{ env.Configuration }} src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj - env: - Credentials:0:MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID}} - Credentials:0:Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} - Credentials:1:MerchantId: ${{ secrets.NORWAY_MERCHANT_ID}} - Credentials:1:Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} - Credentials:2:MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} - Credentials:2:Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} - SampleWebsite:Url: https://localhost:5001 - - - - name: Archive Sample.AspNetCore - uses: actions/upload-artifact@v3 - with: - name: Sample.AspNetCore - path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/samplesite - - - name: Nuget pack - run: dotnet pack ${{ env.CsprojPath }} -p:PackageVersion=${{ needs.variables.outputs.NUGETVERSIONV2 }} -p:Version=${{ needs.variables.outputs.NUGETVERSIONV2 }} --configuration=${{ env.Configuration }} --output=artifacts - - - name: Archive nuget packages artifacts - uses: actions/upload-artifact@v3 - with: - # Artifact name - name: packages - # A file, directory or wildcard pattern that describes what to upload - path: artifacts/*.nupkg - - - name: Publish to nuget - if: ${{ github.event_name == 'release' }} - run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate - - publish: - if: ${{ github.event_name != 'pull_request' }} - name: Publish - needs: [variables, build, test] - runs-on: ubuntu-20.04 - steps: - - name: Download All artifacts - uses: actions/download-artifact@v3 - - # Deploy to Dev Azure Web apps - - name: 'Run Azure webapp deploy to dev using publish profile credentials' - if: ${{ needs.variables.outputs.ENVIRONMENT == 'dev'}} - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ needs.variables.outputs.AZURE_WEBAPP_NAME }} # Replace with your app name - publish-profile: ${{ secrets.AZURE_WEBAPP_DEV_PUBLISH_PROFILE }} # Define secret variable in repository settings as per action documentation - package: 'Sample.AspNetCore' - - - # Deploy to Uat Azure Web apps - - name: 'Run Azure webapp deploy to uat using publish profile credentials' - if: ${{ needs.variables.outputs.ENVIRONMENT == 'uat'}} - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ needs.variables.outputs.AZURE_WEBAPP_NAME }} # Replace with your app name - publish-profile: ${{ secrets.AZURE_WEBAPP_UAT_PUBLISH_PROFILE }} # Define secret variable in repository settings as per action documentation - package: 'Sample.AspNetCore' - - # Deploy to Pro Azure Web apps - - name: 'Run Azure webapp deploy to pro using publish profile credentials' - if: ${{ needs.variables.outputs.ENVIRONMENT == 'pro'}} - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ needs.variables.outputs.AZURE_WEBAPP_NAME }} # Replace with your app name - publish-profile: ${{ secrets.AZURE_WEBAPP_PRO_PUBLISH_PROFILE }} # Define secret variable in repository settings as per action documentation - package: 'Sample.AspNetCore' diff --git a/.github/workflows/deploy_pro_package_samplesite.yml b/.github/workflows/deploy_pro_package_samplesite.yml new file mode 100644 index 00000000..befda3bd --- /dev/null +++ b/.github/workflows/deploy_pro_package_samplesite.yml @@ -0,0 +1,37 @@ +name: Deploy PRO nuget package and samplesite + +on: + workflow_run: + workflows: ["Pro"] + types: + - completed + +jobs: + publish_nuget: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + name: Publish nuget package + runs-on: ubuntu-latest + environment: ${{ github.event.workflow_run.name }} + + steps: + - name: Download artifact + id: download-artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow_conclusion: success + run_id: ${{ github.event.workflow_run.id }} + name: packages + path: packages + + - name: Publish to nuget + run: dotnet nuget push packages/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate + + + publish_sample_site: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + name: Publish sample site + uses: ./.github/workflows/template_publish_webapp.yml + secrets: inherit + with: + Environment: ${{ github.event.workflow_run.name }} + WorkflowRunId: ${{ github.event.workflow_run.id }} \ No newline at end of file diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 00000000..d3251b9c --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,29 @@ +name: Dev + +# Controls when the action will run. Triggers the workflow on push or pull request +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +jobs: + callVersion: + name: Create version number + uses: ./.github/workflows/template_version.yml + + callBuild: + needs: [callVersion] + name: Build and publish artifact + uses: ./.github/workflows/template_build.yml + secrets: inherit + with: + Configuration: Release + NUGETVERSIONV2: ${{ needs.callVersion.outputs.NUGETVERSIONV2 }} + SlnPath: ./src/Svea.WebPay.SDK.sln + SdkCsprojPath: ./src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj + SampleSiteCsprojPath: ./src/Samples/Sample.AspNetCore/Sample.AspNetCore.csproj + SystemTestsCsprojPath: ./src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj + UnitTestsCsprojPath: ./src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj \ No newline at end of file diff --git a/.github/workflows/pro.yml b/.github/workflows/pro.yml new file mode 100644 index 00000000..884911fa --- /dev/null +++ b/.github/workflows/pro.yml @@ -0,0 +1,26 @@ +name: Pro + +# Controls when the action will run. Triggers the workflow on push or pull request +on: + release: + types: + - created + +jobs: + callVersion: + name: Create version number + uses: ./.github/workflows/template_version.yml + + callBuild: + needs: [callVersion] + name: Build and publish artifact + uses: ./.github/workflows/template_build.yml + secrets: inherit + with: + Configuration: Release + NUGETVERSIONV2: ${{ needs.callVersion.outputs.NUGETVERSIONV2 }} + SlnPath: ./src/Svea.WebPay.SDK.sln + SdkCsprojPath: ./src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj + SampleSiteCsprojPath: ./src/Samples/Sample.AspNetCore/Sample.AspNetCore.csproj + SystemTestsCsprojPath: ./src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj + UnitTestsCsprojPath: ./src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj \ No newline at end of file diff --git a/.github/workflows/template_build.yml b/.github/workflows/template_build.yml new file mode 100644 index 00000000..683e0c80 --- /dev/null +++ b/.github/workflows/template_build.yml @@ -0,0 +1,92 @@ +name: Build + +on: + workflow_call: + inputs: + Configuration: + required: true + type: string + NUGETVERSIONV2: + required: true + type: string + SlnPath: + required: true + type: string + SdkCsprojPath: + required: true + type: string + SampleSiteCsprojPath: + required: true + type: string + SystemTestsCsprojPath: + required: true + type: string + UnitTestsCsprojPath: + required: true + type: string + +env: + dotnetVersion: | + 3.1.101 + 5.x + 6.x + AZURE_WEBAPP_PACKAGE_PATH: '.' + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.dotnetVersion }} + + - name: Install dependencies + run: dotnet restore ${{ inputs.SlnPath }} + + - name: Publish Sdk Sample site + run: dotnet publish --configuration ${{ inputs.Configuration }} -p:Version=${{ inputs.nuGetVersionV2 }} --no-restore ${{ inputs.SampleSiteCsprojPath }} --output '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/samplesite' + + - name: Archive Sample.AspNetCore + uses: actions/upload-artifact@v3 + with: + name: Sample.AspNetCore + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/samplesite + + - name: Publish Sdk Unit tests + run: dotnet publish --configuration ${{ inputs.Configuration }} -p:Version=${{ inputs.nuGetVersionV2 }} --no-restore ${{ inputs.UnitTestsCsprojPath }} --output '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/unittests' + + - name: Archive Sdk Unit tests + uses: actions/upload-artifact@v3 + with: + name: Sdk.UnitTests + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/unittests + + - name: Verify chrome driver + shell: pwsh + run: | + ./src/Samples/Sample.AspNetCore.SystemTests/chrome-driver-udpate.ps1 + + - name: Publish Sdk Sample site system test + run: dotnet publish --configuration ${{ inputs.Configuration }} -p:Version=${{ inputs.nuGetVersionV2 }} --no-restore ${{ inputs.SystemTestsCsprojPath }} --output '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/systemtests' + + - name: Archive Sample.AspNetCore.SystemTests + uses: actions/upload-artifact@v3 + with: + name: Sample.AspNetCore.SystemTests + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/systemtests + + - name: Nuget pack + run: dotnet pack ${{ inputs.SdkCsprojPath }} -p:PackageVersion=${{ inputs.NUGETVERSIONV2 }} -p:Version=${{ inputs.NUGETVERSIONV2 }} --no-restore --configuration=${{ inputs.Configuration }} --output=artifacts + + - name: Archive nuget packages artifacts + uses: actions/upload-artifact@v3 + with: + # Artifact name + name: packages + # A file, directory or wildcard pattern that describes what to upload + path: artifacts/*.nupkg \ No newline at end of file diff --git a/.github/workflows/template_publish_webapp.yml b/.github/workflows/template_publish_webapp.yml new file mode 100644 index 00000000..ff594a43 --- /dev/null +++ b/.github/workflows/template_publish_webapp.yml @@ -0,0 +1,53 @@ +name: Publish WebApp + +on: + workflow_call: + inputs: + Environment: + required: true + type: string + WorkflowRunId: + required: true + type: string + +jobs: + publish_sample_site: + name: Publish sample site + runs-on: ubuntu-latest + environment: ${{ inputs.Environment }} + + steps: + - name: Download artifact + id: download-artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow_conclusion: success + run_id: ${{ inputs.WorkflowRunId }} + name: Sample.AspNetCore + path: Sample.AspNetCore + + - name: Variable Substitution + uses: microsoft/variable-substitution@v1 + with: + # comma separated list of XML/JSON/YAML files in which tokens are to be substituted. Files names must be specified relative to the folder-path. + files: 'Sample.AspNetCore/appsettings.json' + env: + Credentials.0.MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID }} + Credentials.0.Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} + Credentials.1.MerchantId: ${{ secrets.NORWAY_MERCHANT_ID }} + Credentials.1.Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} + Credentials.2.MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} + Credentials.2.Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} + MerchantSettings.PushUri: https://svea-webpay-sdk-001-${{ inputs.Environment }}.azurewebsites.net/api/svea/push/{checkout.order.uri}/?marketId={marketId} + MerchantSettings.TermsUri: https://svea-webpay-sdk-001-${{ inputs.Environment }}.azurewebsites.net/terms + MerchantSettings.CheckoutUri: https://svea-webpay-sdk-001-${{ inputs.Environment }}.azurewebsites.net/CheckOut/LoadPaymentMenu + MerchantSettings.ConfirmationUri: https://svea-webpay-sdk-001-${{ inputs.Environment }}.azurewebsites.net/checkout/thankyou + MerchantSettings.CheckoutValidationCallbackUri: https://svea-webpay-sdk-001-${{ inputs.Environment }}.azurewebsites.net/api/svea/validation/{checkout.order.uri}/?marketId={marketId} + MerchantSettings.WebhookUri: https://svea-webpay-sdk-001-${{ inputs.Environment }}.azurewebsites.net/api/svea/shippingvalidation/?marketId={marketId} + + - name: 'Run Azure webapp deploy using publish profile credentials' + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ secrets.AZURE_WEBAPP_NAME }} # Replace with your app name + publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }} # Define secret variable in repository settings as per action documentation + package: 'Sample.AspNetCore' \ No newline at end of file diff --git a/.github/workflows/template_version.yml b/.github/workflows/template_version.yml new file mode 100644 index 00000000..f43ebe1d --- /dev/null +++ b/.github/workflows/template_version.yml @@ -0,0 +1,29 @@ +name: Create version number + +on: + workflow_call: + outputs: + NUGETVERSIONV2: + description: "Nuget version" + value: ${{ jobs.version.outputs.NUGETVERSIONV2 }} + +jobs: + version: + runs-on: ubuntu-latest + outputs: + NUGETVERSIONV2: ${{ steps.gitversion.outputs.NUGETVERSIONV2 }} + + steps: + - uses: actions/checkout@v3 + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.9.13 + with: + versionSpec: '5.x.x' + + - name: Fetch all history for all tags and branches + run: git fetch --prune --unshallow + + - name: Use GitVersion + id: gitversion # step id used as reference for output values + uses: gittools/actions/gitversion/execute@v0.9.13 \ No newline at end of file diff --git a/.github/workflows/test_deploy_samplesite.yml b/.github/workflows/test_deploy_samplesite.yml new file mode 100644 index 00000000..e6ab9a33 --- /dev/null +++ b/.github/workflows/test_deploy_samplesite.yml @@ -0,0 +1,145 @@ +name: Run unit and ui tests, deploy samplesite +on: + workflow_run: + workflows: ["Dev", "Uat"] + types: + - completed + +env: + dotnetVersion: | + 3.1.101 + 5.x + 6.x + +jobs: + test-unit-ui: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + environment: ${{ github.event.workflow_run.name }} + env: + uitestUrlSubdomain: uitest-${{ github.event.workflow_run.id }}-svea-webpay-sdk + outputs: + sourceHeadRepo: ${{ steps.source-run-info.outputs.sourceHeadRepo }} + sourceHeadBranch: ${{ steps.source-run-info.outputs.sourceHeadBranch }} + sourceHeadSha: ${{ steps.source-run-info.outputs.sourceHeadSha }} + mergeCommitSha: ${{ steps.source-run-info.outputs.mergeCommitSha }} + targetCommitSha: ${{ steps.source-run-info.outputs.targetCommitSha }} + pullRequestNumber: ${{ steps.source-run-info.outputs.pullRequestNumber }} + pullRequestLabels: ${{ steps.source-run-info.outputs.pullRequestLabels }} + targetBranch: ${{ steps.source-run-info.outputs.targetBranch }} + sourceEvent: ${{ steps.source-run-info.outputs.sourceEvent }} + + steps: + - name: "Get information about the origin 'CI' run" + uses: potiuk/get-workflow-origin@v1_1 + id: source-run-info + with: + token: ${{ secrets.GITHUB_TOKEN }} + sourceRunId: ${{ github.event.workflow_run.id }} + + - name: Download artifact + id: download-artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow_conclusion: success + run_id: ${{ github.event.workflow_run.id }} + + - name: Variable Substitution + uses: microsoft/variable-substitution@v1 + with: + # comma separated list of XML/JSON/YAML files in which tokens are to be substituted. Files names must be specified relative to the folder-path. + files: 'Sample.AspNetCore/ngrok.yml' + env: + authtoken: ${{ secrets.NGROK_AUTHTOKEN }} + tunnels.uitestGithub.subdomain: ${{ env.uitestUrlSubdomain }} + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.dotnetVersion }} + + - name: Run Unit Tests + run: dotnet test Sdk.UnitTests/Svea.WebPay.SDK.Tests.dll + env: + Credentials:0:MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID}} + Credentials:0:Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} + Credentials:1:MerchantId: ${{ secrets.NORWAY_MERCHANT_ID}} + Credentials:1:Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} + Credentials:2:MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} + Credentials:2:Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} + + - name: Setup ngrok + run: | + wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip + jar xvf ngrok-stable-linux-amd64.zip + chmod +x ngrok + + - name: Start ngrok + run: ./ngrok start -config Sample.AspNetCore/ngrok.yml uitestGithub & + + - name: Start SDK Sample project + working-directory: Sample.AspNetCore + run: + dotnet Sample.AspNetCore.dll & + env: + Credentials:0:MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID}} + Credentials:0:Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} + Credentials:1:MerchantId: ${{ secrets.NORWAY_MERCHANT_ID}} + Credentials:1:Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} + Credentials:2:MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} + Credentials:2:Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} + MerchantSettings:PushUri: https://${{ env.uitestUrlSubdomain }}.eu.ngrok.io/api/svea/push/{checkout.order.uri}/?marketId={marketId} + MerchantSettings:TermsUri: https://${{ env.uitestUrlSubdomain }}.eu.ngrok.io/terms + MerchantSettings:CheckoutUri: https://${{ env.uitestUrlSubdomain }}.eu.ngrok.io/CheckOut/LoadPaymentMenu + MerchantSettings:ConfirmationUri: https://${{ env.uitestUrlSubdomain }}.eu.ngrok.io/checkout/thankyou + MerchantSettings:CheckoutValidationCallbackUri: https://${{ env.uitestUrlSubdomain }}.eu.ngrok.io/api/svea/validation/{checkout.order.uri}/?marketId={marketId} + MerchantSettings:WebhookUri: https://${{ env.uitestUrlSubdomain }}.eu.ngrok.io/api/svea/shippingvalidation/?marketId={marketId} + + - name: Run SDK UI tests + run: dotnet test Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.dll + env: + Credentials:0:MerchantId: ${{ secrets.SWEDEN_MERCHANT_ID}} + Credentials:0:Secret: ${{ secrets.SWEDEN_MERCHANT_SECRET }} + Credentials:1:MerchantId: ${{ secrets.NORWAY_MERCHANT_ID}} + Credentials:1:Secret: ${{ secrets.NORWAY_MERCHANT_SECRET }} + Credentials:2:MerchantId: ${{ secrets.FINNISH_MERCHANT_ID }} + Credentials:2:Secret: ${{ secrets.FINNISH_MERCHANT_SECRET }} + SampleWebsite:Url: https://localhost:5001 + + - name: Commit Action Status + if: ${{ always() }} + uses: LouisBrunner/checks-action@v1.3.1 + with: + sha: ${{ steps.source-run-info.outputs.sourceHeadSha }} + token: ${{ secrets.GITHUB_TOKEN }} + name: Run Unit and UI tests + conclusion: ${{ job.status }} + + publish_alpha_nuget: + needs: [test-unit-ui] + if: ${{ needs.test-unit-ui.outputs.sourceEvent != 'pull_request' && github.event.workflow_run.name == 'Dev' }} + name: Publish alpha nuget + runs-on: ubuntu-latest + environment: ${{ github.event.workflow_run.name }} + + steps: + - name: Download artifact + id: download-artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow_conclusion: success + run_id: ${{ github.event.workflow_run.id }} + name: packages + path: packages + + - name: Publish test package to GitHub Package Registry + run: dotnet nuget push "packages/*.nupkg" --api-key ${{secrets.GITHUB_TOKEN}} --source https://nuget.pkg.github.com/sveawebpay/index.json --skip-duplicate + + publish_sample_site: + needs: [test-unit-ui] + if: ${{ needs.test-unit-ui.outputs.sourceEvent != 'pull_request' }} + uses: ./.github/workflows/template_publish_webapp.yml + secrets: inherit + with: + Environment: ${{ github.event.workflow_run.name }} + WorkflowRunId: ${{ github.event.workflow_run.id }} \ No newline at end of file diff --git a/.github/workflows/uat.yml b/.github/workflows/uat.yml new file mode 100644 index 00000000..7f0c5d1f --- /dev/null +++ b/.github/workflows/uat.yml @@ -0,0 +1,27 @@ +name: Uat + +# Controls when the action will run. Triggers the workflow on push or pull request +on: + push: + branches: + - release/** + - hotfix/** + +jobs: + callVersion: + name: Create version number + uses: ./.github/workflows/template_version.yml + + callBuild: + needs: [callVersion] + name: Build and publish artifact + uses: ./.github/workflows/template_build.yml + secrets: inherit + with: + Configuration: Release + NUGETVERSIONV2: ${{ needs.callVersion.outputs.NUGETVERSIONV2 }} + SlnPath: ./src/Svea.WebPay.SDK.sln + SdkCsprojPath: ./src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj + SampleSiteCsprojPath: ./src/Samples/Sample.AspNetCore/Sample.AspNetCore.csproj + SystemTestsCsprojPath: ./src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj + UnitTestsCsprojPath: ./src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj \ No newline at end of file diff --git a/.gitignore b/.gitignore index b2fdbb45..43688d70 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin .vs /src/Svea.WebPay.SDK.Extensions packages +*.user diff --git a/README.md b/README.md index 836f7fdd..a5812c69 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ * [3. Configuration] * [4. Supported APIs] * [5. Polling of Tasks] +* [6. Shipping Module] ## 1. Introduction @@ -49,7 +50,8 @@ Step 1: "TermsUri": "https://{Your domain}/terms", "CheckoutUri": "https://{Your domain}/CheckOut/LoadPaymentMenu", "ConfirmationUri": "https://{Your domain}/checkout/thankyou", - "CheckoutValidationCallbackUri": "https://{Your domain}/validation/{checkout.order.uri}" + "CheckoutValidationCallbackUri": "https://{Your domain}/validation/{checkout.order.uri}", + "WebhookUri": "https://{Your domain}/shippingvalidation" // Used for shipping callback } } @@ -123,3 +125,30 @@ When executing theese actions **'Add order row'**, **'Add order rows'**, **'Deli for the specific action you have the option to specify a 'Polling timeout (TimeSpan)'. This will allow the SDK to poll the task until the timeout is reached or the task is done. Then the resource object will be returned. If not specified the SDK will try once if the Task has not finished the Task Uri will be returned and the polling needs to be done by the user. +## 6. Shipping Module + +**Shipping VAT calculations** + +For VAT calculation callback the model **ShippingOption** can be used as parameter. Shipping VAT calculation can then be performed using **CalculateShippingOrderRows** on the **Cart**. + +**Example** + +```csharp + + public async Task ShippingTaxCalculation(ShippingOption shippingOption) + { + // Get order + var order = await _sveaClient.Checkout.GetOrder(shippingOption.OrderId).ConfigureAwait(false); + + // Calculate shipping VAT + order.Cart.CalculateShippingOrderRows(shippingOption); + + // Update order + await _sveaClient.Checkout.UpdateOrder(order.OrderId, new UpdateOrderModel(order.Cart, null, order.ShippingInformation)).ConfigureAwait(false); + } + +``` + +**WebhookUri Callback / Shipping callback** + +For validation of shipping callback the model **ShippingCallbackResponse** can be used as a parameter. diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Orders/Blocks/OrderBlock.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Orders/Blocks/OrderBlock.cs index 8e3bdf7e..28176903 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Orders/Blocks/OrderBlock.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Orders/Blocks/OrderBlock.cs @@ -14,6 +14,12 @@ public class OrderBlock : Control where TOwner : PageObject PaymentType { get; private set; } + + [FindByAutomation("text-shippingstatus")] + public Text ShippingStatus { get; private set; } + + [FindByAutomation("text-shippingdescription")] + public Text ShippingDescription { get; private set; } public OrderTable Table { get; private set; } } diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/AddShippingBlock.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/AddShippingBlock.cs new file mode 100644 index 00000000..830b7f66 --- /dev/null +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/AddShippingBlock.cs @@ -0,0 +1,11 @@ +using Atata; + +namespace Sample.AspNetCore.SystemTests.PageObjectModels.Payment.Blocks +{ + [ControlDefinition("*[@data-testid='add-shipping-address-link']", ComponentTypeName = "Add Shipping Block")] + public class AddShippingBlock : Control where TOwner : PageObject + { + [FindByCss("*[data-testid='expandable-card-header']")] + public Clickable Expand { get; private set; } + } +} diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/EditShippingBlock.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/EditShippingBlock.cs new file mode 100644 index 00000000..1b00467d --- /dev/null +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/EditShippingBlock.cs @@ -0,0 +1,32 @@ +using Atata; + +namespace Sample.AspNetCore.SystemTests.PageObjectModels.Payment.Blocks +{ + [ControlDefinition("*[@data-testid='edit-address-view']", ComponentTypeName = "Edit Address Block")] + public class EditShippingBlock : Control where TOwner : PageObject + { + [FindByCss("*[data-testid='new-address']")] + public Clickable NewAddress { get; private set; } + + [FindByCss("*[data-testid='address-form-fields-b2c']")] + public Control AddressForm { get; private set; } + + [FindByName("firstName")] + public TextInput FirstName { get; private set; } + + [FindByName("lastName")] + public TextInput LastName { get; private set; } + + [FindByName("streetAddress")] + public TextInput StreetAddress { get; private set; } + + [FindByName("postalCode")] + public TelInput ZipCode { get; private set; } + + [FindByName("city")] + public TextInput City { get; private set; } + + [FindByCss("*[data-testid='submit-button']")] + public Button Submit { get; private set; } + } +} diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/SelectShippingBlock.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/SelectShippingBlock.cs new file mode 100644 index 00000000..9e1d54ba --- /dev/null +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Blocks/SelectShippingBlock.cs @@ -0,0 +1,53 @@ +using Atata; + +namespace Sample.AspNetCore.SystemTests.PageObjectModels.Payment.Blocks +{ + [ControlDefinition("my-shipping", ComponentTypeName = "Select Shipping Block")] + public class SelectShippingBlock : Control where TOwner : PageObject + { + public ControlList, TOwner> Options { get; private set; } + + [FindByCss("*[data-testid='clear-selected-option-btn']")] + public Button ChangeCarrier { get; private set; } + + [FindByCss("*[data-testid='change-location-link']")] + public Button ChangePickupPlace { get; private set; } + + public ControlList, TOwner> PickupList { get; private set; } + + [FindByContent("Bekräfta")] + public Button ConfirmChange { get; private set; } + + [FindById("fields.FCDELIVERYINSTRUCTIONS")] + public Input Instructions { get; private set; } + + [FindByCss("*[data-testid='confirm-shipping-option']")] + public Button Submit { get; private set; } + + + #region Bring + + [FindById("fields.FCRECEIVERDOORCODE")] + public Input DoorCode { get; private set; } + + #endregion + + #region Postnord + + [FindByCss("*[data-testid='FCDELIVERYTIME12']")] + public CheckBox DeliveryBefore12 { get; private set; } + + [FindByCss("*[data-testid='FCNOTIFYPHONE']")] + public CheckBox CallBeforeDelivery { get; private set; } + + #endregion + + #region Budbee + + [FindByCss("*[data-testid='FCINDOOR']")] + public Clickable Indoor { get; private set; } + + #endregion + + } +} diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Items/ShippingOptionItem.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Items/ShippingOptionItem.cs new file mode 100644 index 00000000..6f266945 --- /dev/null +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/Items/ShippingOptionItem.cs @@ -0,0 +1,10 @@ +using Atata; + +namespace Sample.AspNetCore.SystemTests.PageObjectModels.Payment +{ + [ControlDefinition("div[@data-testid='expandable-card-header']", ComponentTypeName = "Shipping Option Item", Visibility = Visibility.Visible)] + public class ShippingOptionItem : Control where TOwner : PageObject + { + public Text Text { get; private set; } + } +} diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/SveaPaymentFramePage.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/SveaPaymentFramePage.cs index 0c98f2ef..af349ec0 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/SveaPaymentFramePage.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Payment/SveaPaymentFramePage.cs @@ -1,5 +1,6 @@ using Atata; using Sample.AspNetCore.SystemTests.PageObjectModels.Payment; +using Sample.AspNetCore.SystemTests.PageObjectModels.Payment.Blocks; namespace Sample.AspNetCore.SystemTests.PageObjectModels { @@ -19,6 +20,12 @@ public class SveaPaymentFramePage : Page<_> public DetailsB2CAnonymousBlock<_> B2CAnonymous { get; private set; } + public AddShippingBlock<_> AddShippingBlock { get; private set; } + + public EditShippingBlock<_> EditShippingBlock { get; private set; } + + public SelectShippingBlock<_> SelectShippingBlock { get; private set; } + public PaymentMethodsBlock<_> PaymentMethods { get; private set; } public LeasingPaymentBlock<_> Leasing { get; private set; } @@ -37,6 +44,10 @@ public class SveaPaymentFramePage : Page<_> [FindByCss("div[data-testid='bankid-dialog']")] public Control<_> BankId{ get; private set; } + [FindByCss("button[data-testid='confirm-button']")] + public Button<_> ConfirmBankId { get; private set; } + + public Frame CardPaymentFramePage { get; set; } } } \ No newline at end of file diff --git a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Products/ProductsPage.cs b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Products/ProductsPage.cs index bd459e8b..40205077 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Products/ProductsPage.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/PageObjectModels/Products/ProductsPage.cs @@ -24,6 +24,9 @@ public class ProductsPage : BasePage<_> [FindByAutomation("button-checkout-international")] public Link InternationalCheckout { get; set; } + [FindByAutomation("button-checkout-shipping")] + public Link ShippingCheckout { get; set; } + [FindByXPath("table[2]//tfoot[1]//td[2]")] public Text<_> TotalAmount { get; set; } diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj b/src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj index 748c7b78..997bc813 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj +++ b/src/Samples/Sample.AspNetCore.SystemTests/Sample.AspNetCore.SystemTests.csproj @@ -34,9 +34,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Services/TestDataService.cs b/src/Samples/Sample.AspNetCore.SystemTests/Services/TestDataService.cs index 335587a8..22d61e23 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Services/TestDataService.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Services/TestDataService.cs @@ -21,8 +21,10 @@ public static class TestDataService public static string LoremIpsum => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent convallis facilisis neque ut scelerisque. Morbi arcu purus, gravida sed velit nec, interdum egestas ante. Pellentesque dapibus nisl ultrices dolor placerat, eu lobortis mauris elementum. Curabitur placerat ante est. Fusce et massa est. Etiam quis lacus justo. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus nulla enim, ornare in facilisis quis, ornare nec erat. Nullam sit amet mi augue. Proin dignissim risus urna, sed pulvinar turpis sollicitudin quis. Proin pretium lacinia ullamcorper."; - public static string OrganizationNumber => "5590326186"; - + public static string OrganizationNumberLeasing => "5590326186"; + + public static string OrganizationNumber => "5620202027"; + public static string SwedishPersonalNumber => "194605092222"; public static string SwedishPersonalNumberShort => "4605092222"; @@ -43,6 +45,10 @@ public static class TestDataService public static string Reference => "Ref123"; + public static string ShippingInstructions => "Test instructions"; + + public static string DoorCode => "1234"; + public static string NorwegianFirstName => "Ola"; public static string NorwegianLastName => "Normann"; @@ -52,12 +58,17 @@ public static class TestDataService public static string NorwegianCity => "Oslo"; public static string NorwegianPhoneNumber => "17054512"; + public static string ShippingStreetAddress = "Roslagsgatan 10"; + public static string ShippingZipCode = "113 51"; + public static string ShippingCity = "Stockholm"; + public static string FinnishZipCode => "11111"; public static string FinnishPhoneNumber => "43554343"; public static string FinnishPersonalNumber => "290296-7808"; + public static string Description(int length = 30) { return LoremIpsum.Substring(0, length); diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/Base/TestBase.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/Base/TestBase.cs index bef0638e..a48fa51b 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Test/Base/TestBase.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/Base/TestBase.cs @@ -13,7 +13,6 @@ namespace Sample.AspNetCore.SystemTests.Test.Base { - using static Drivers; [TestFixture(DriverAliases.Chrome)] diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/IdentificationHelper.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/IdentificationHelper.cs index 5ebb6ee5..ea21b41e 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/IdentificationHelper.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/IdentificationHelper.cs @@ -65,12 +65,12 @@ public static SveaPaymentFramePage ProceedWithFinnishPrivateIdentification(this }); } - public static SveaPaymentFramePage ProceedWithCompanyIdentification(this SveaPaymentFramePage page) + public static SveaPaymentFramePage ProceedWithCompanyIdentification(this SveaPaymentFramePage page, string org) { return page .Entity.IsCompany.Click() .B2BIdentification.Email.Set(TestDataService.CompanyEmail) - .B2BIdentification.OrganizationNumber.Set(TestDataService.OrganizationNumber) + .B2BIdentification.OrganizationNumber.Set(org) .Submit.Click() .Do(x => { diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/PaymentHelper.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/PaymentHelper.cs index dc3c2fa0..3844344e 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/PaymentHelper.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/PaymentHelper.cs @@ -13,6 +13,8 @@ public static SveaPaymentFramePage PayWithCard(this SveaPaymentFramePage page, b { return page .PaymentMethods.Card.IsVisible.WaitTo.BeTrue() + .WaitSeconds(1) + .PaymentMethods.Card.Focus() .PaymentMethods.Card.Click() .Do(x => { @@ -83,6 +85,7 @@ public static SveaPaymentFramePage PayWithDirektBank(this SveaPaymentFramePage p .PaymentMethods.DirektBank.IsVisible.WaitTo.BeTrue() .PaymentMethods.DirektBank.Click() .PaymentMethods.DirektBank.Nordea.Click() + .WaitSeconds(1) .Submit.Click(); } @@ -91,7 +94,17 @@ public static SveaPaymentFramePage PayWithInvoice(this SveaPaymentFramePage page return page .PaymentMethods.Invoice.IsVisible.WaitTo.BeTrue() .PaymentMethods.Invoice.Click() - .Submit.Click(); + .WaitSeconds(2) + .Submit.Focus() + .Submit.Click() + .WaitSeconds(5) + .Do(x => + { + if (page.BankId.IsVisible) + { + page.ConfirmBankId.Click(); + } + }); } public static SveaPaymentFramePage PayWithTrustly(this SveaPaymentFramePage page, Checkout.Option checkout) @@ -137,6 +150,7 @@ public static SveaPaymentFramePage PayWithPaymentPlan(this SveaPaymentFramePage .PaymentMethods.PaymentPlan.IsVisible.WaitTo.BeTrue() .PaymentMethods.PaymentPlan.Click() .PaymentMethods.PaymentPlan.Options[1].Click() + .WaitSeconds(1) .Submit.Click(); } @@ -145,6 +159,7 @@ public static SveaPaymentFramePage PayWithAccount(this SveaPaymentFramePage page return page .PaymentMethods.Account.IsVisible.WaitTo.BeTrue() .PaymentMethods.Account.Click() + .WaitSeconds(1) .Submit.Click(); } @@ -161,6 +176,7 @@ public static SveaPaymentFramePage PayWithSwish(this SveaPaymentFramePage page) return page .PaymentMethods.Swish.IsVisible.WaitTo.BeTrue() .PaymentMethods.Swish.Click() + .WaitSeconds(1) .Submit.Click(); } @@ -169,6 +185,7 @@ public static SveaPaymentFramePage PayWithVipps(this SveaPaymentFramePage page) return page .PaymentMethods.Vipps.IsVisible.WaitTo.BeTrue() .PaymentMethods.Vipps.Click() + .WaitSeconds(1) .Submit.ClickAndGo() .Next.Click() .SwitchToRoot(); @@ -181,8 +198,11 @@ public static SveaPaymentFramePage PayWithLeasing(this SveaPaymentFramePage page .Leasing.SixtyMonths.Click() .Leasing.Continue.Click() .Leasing.Email.Set(TestDataService.CompanyEmail) - .Submit.Click() - .Leasing.ManualConfirmation.WaitTo.WithinSeconds(10).BeVisible() + .WaitSeconds(1) + .PaymentMethods.Reference.Focus() + .Press(Keys.Tab) + .Press(Keys.Space) + .Leasing.ManualConfirmation.WaitTo.WithinSeconds(15).BeVisible() .Leasing.Confirm.Click(); } diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/ShippingOptions.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/ShippingOptions.cs new file mode 100644 index 00000000..7a96d1ad --- /dev/null +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/ShippingOptions.cs @@ -0,0 +1,13 @@ +namespace Sample.AspNetCore.SystemTests.Test.Helpers +{ + public class ShippingOptions + { + public const string Bring = "Bring PickUp Parcel"; + + public const string Plab = "PostNord Parcel"; + + public const string Dhl = "DHL Home Delivery"; + + public const string Budbee = "Budbee"; + } +} diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/SveaPaymentFrameHelper.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/SveaPaymentFrameHelper.cs index 700f9ef3..20908b2a 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/SveaPaymentFrameHelper.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/Helpers/SveaPaymentFrameHelper.cs @@ -1,12 +1,15 @@ using Atata; +using OpenQA.Selenium; using Sample.AspNetCore.SystemTests.PageObjectModels; using Sample.AspNetCore.SystemTests.Services; +using System.Collections.Generic; +using System.Linq; namespace Sample.AspNetCore.SystemTests.Test.Helpers { public static class SveaPaymentFrameHelper { - public static SveaPaymentFramePage IdentifyEntity(this SveaPaymentFramePage page, Checkout.Option checkout = Checkout.Option.Identification, Entity.Option entity = Entity.Option.Private, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) + public static SveaPaymentFramePage IdentifyEntity(this SveaPaymentFramePage page, Checkout.Option checkout = Checkout.Option.Identification, Entity.Option entity = Entity.Option.Private, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card, Dictionary shipping = null) { page.Entity.IsVisible.WaitTo.BeTrue(); @@ -22,7 +25,17 @@ public static SveaPaymentFramePage IdentifyEntity(this SveaPaymentFramePage page if(paymentMethod != PaymentMethods.Option.Vipps) { - return page.ProceedWithSwedishPrivateIdentification().PaymentMethods.IsVisible.WaitTo.BeTrue(); + return page.ProceedWithSwedishPrivateIdentification().Do(x => + { + if (shipping != null) + { + x.AddShippingBlock.IsVisible.WaitTo.BeTrue(); + } + else + { + x.PaymentMethods.IsVisible.WaitTo.BeTrue(); + } + }); } else if(paymentMethod == PaymentMethods.Option.MobilePay) { @@ -30,7 +43,17 @@ public static SveaPaymentFramePage IdentifyEntity(this SveaPaymentFramePage page } else { - return page.ProceedWithNorwegianPrivateIdentification().PaymentMethods.IsVisible.WaitTo.BeTrue(); + return page.ProceedWithNorwegianPrivateIdentification().Do(x => + { + if (shipping != null) + { + x.AddShippingBlock.IsVisible.WaitTo.BeTrue(); + } + else + { + x.PaymentMethods.IsVisible.WaitTo.BeTrue(); + } + }); } case Checkout.Option.Anonymous: @@ -43,7 +66,14 @@ public static SveaPaymentFramePage IdentifyEntity(this SveaPaymentFramePage page { default: case Checkout.Option.Identification: - return page.ProceedWithCompanyIdentification().PaymentMethods.IsVisible.WaitTo.BeTrue(); + if(paymentMethod == PaymentMethods.Option.Leasing) + { + return page.ProceedWithCompanyIdentification(TestDataService.OrganizationNumberLeasing).PaymentMethods.IsVisible.WaitTo.BeTrue(); + } + else + { + return page.ProceedWithCompanyIdentification(TestDataService.OrganizationNumber).PaymentMethods.IsVisible.WaitTo.BeTrue(); + } case Checkout.Option.Anonymous: return page.ProceedWithCompanyAnonymous().PaymentMethods.IsVisible.WaitTo.BeTrue(); @@ -51,6 +81,130 @@ public static SveaPaymentFramePage IdentifyEntity(this SveaPaymentFramePage page } } + public static SveaPaymentFramePage EditShipping(this SveaPaymentFramePage page, Dictionary shipping) + { + return page.AddShippingBlock.IsVisible.WaitTo.BeTrue() + .AddShippingBlock.Expand.Click() + .EditShippingBlock.NewAddress.IsVisible.WaitTo.BeTrue() + .EditShippingBlock.NewAddress.Click() + .WaitSeconds(2) + .EditShippingBlock.FirstName.Clear() + .EditShippingBlock.FirstName.Set(TestDataService.SwedishFirstName) + .EditShippingBlock.LastName.Clear() + .EditShippingBlock.LastName.Set(TestDataService.SwedishLastName) + .EditShippingBlock.StreetAddress.Clear() + .EditShippingBlock.StreetAddress.Set(TestDataService.ShippingStreetAddress) + .EditShippingBlock.ZipCode.Clear() + .EditShippingBlock.ZipCode.Set(TestDataService.ShippingZipCode) + .WaitSeconds(1) + .Do(x => + { + if (x.EditShippingBlock.City.Value.Length == 0) + { + x.EditShippingBlock.City.Set(TestDataService.ShippingCity); + } + }) + .EditShippingBlock.Submit.Focus() + .EditShippingBlock.Submit.Click() + .Do(x => + { + if (shipping.ContainsKey("carrier")) + { + var options = shipping["carrier"].ToList(); + + foreach (var option in options) + { + if (options.IndexOf(option) != 0) + { + x + .SelectShippingBlock.ChangeCarrier.Click() + .WaitSeconds(1); + } + + x.SelectShippingBlock.Options.WaitTo.WithinSeconds(15).Not.BeEmpty(); + + x + .SelectShippingBlock.Options.FirstOrDefault(x => x.Text.Content.Value.Contains(option)).Click() + .WaitSeconds(1); + } + } + + if (shipping.ContainsKey("pickup")) + { + var pickups = shipping["pickup"].ToList(); + + foreach (var pickup in pickups) + { + x.SelectShippingBlock.ChangePickupPlace.Click(); + x.WaitSeconds(1); + + var index = int.Parse(pickup); + + if(index == -1) + { + x.SelectShippingBlock.PickupList.Last().Click(); + } + else + { + x.SelectShippingBlock.PickupList[index].Click(); + } + + x + .WaitSeconds(1) + .SelectShippingBlock.ConfirmChange.Focus() + .Press(Keys.Space); + } + } + }) + .WaitSeconds(2) + .Do(x => + { + switch (shipping["carrier"].Last()) + { + default: + case ShippingOptions.Bring: + x + .SelectShippingBlock.DoorCode.Set(TestDataService.DoorCode) + .SelectShippingBlock.Instructions.Set(TestDataService.ShippingInstructions) + .WaitSeconds(1) + .SelectShippingBlock.Submit.Focus() + .SelectShippingBlock.Submit.Click(); + break; + + case ShippingOptions.Plab: + x + .SelectShippingBlock.Instructions.Set(TestDataService.ShippingInstructions) + .WaitSeconds(1) + .SelectShippingBlock.DeliveryBefore12.Click() + .WaitSeconds(1) + .SelectShippingBlock.CallBeforeDelivery.Click() + .WaitSeconds(1) + .SelectShippingBlock.Submit.Focus() + .SelectShippingBlock.Submit.Click(); + break; + + case ShippingOptions.Dhl: + x + .SelectShippingBlock.Instructions.Set(TestDataService.ShippingInstructions) + .SelectShippingBlock.DoorCode.Set(TestDataService.DoorCode) + .WaitSeconds(1) + .SelectShippingBlock.Submit.Focus() + .SelectShippingBlock.Submit.Click(); + break; + + case ShippingOptions.Budbee: + x + .SelectShippingBlock.Instructions.Set(TestDataService.ShippingInstructions) + .SelectShippingBlock.Indoor.Click() + .WaitSeconds(1) + .SelectShippingBlock.Submit.Focus() + .SelectShippingBlock.Submit.Click(); + break; + } + }) + ; + } + public static SveaPaymentFramePage Pay(this SveaPaymentFramePage page, Checkout.Option checkout = Checkout.Option.Identification, Entity.Option entity = Entity.Option.Private, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card, string amount = null, bool switchFrame = false) { switch (paymentMethod) @@ -102,10 +256,6 @@ public static SveaPaymentFramePage Pay(this SveaPaymentFramePage page, Checkout. break; } - //page - // .PaymentMethods.TotalAmount.IsVisible.WaitTo.BeTrue() - // .PaymentMethods.TotalAmount.Should.ContainAmount(amount); - if (entity == Entity.Option.Company && checkout == Checkout.Option.Identification) { page.WaitSeconds(1) diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Base/PaymentTests.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Base/PaymentTests.cs index bf0a77cd..ef666935 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Base/PaymentTests.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Base/PaymentTests.cs @@ -37,10 +37,10 @@ public PaymentTests(string driverAlias) public void OneTimeSetUp() { var config = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .AddUserSecrets("6343ea20-120b-4075-a141-c8154cad1d14") - .AddEnvironmentVariables() - .Build(); + .AddJsonFile("appsettings.json") + .AddUserSecrets("6343ea20-120b-4075-a141-c8154cad1d14") + .AddEnvironmentVariables() + .Build(); var handler = new HttpClientHandler { @@ -75,7 +75,9 @@ public void OneTimeSetUp() ); } - protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) + protected ProductsPage SelectProducts( + Product[] products, + PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) { _amount = 0; @@ -89,7 +91,7 @@ protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option .Header.Products.ClickAndGo(); } - switch(paymentMethod) + switch (paymentMethod) { case PaymentMethods.Option.Vipps: x.Market.Click(); @@ -102,8 +104,8 @@ protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option x.Markets[m => m.Content.Value == "FI"].Click(); break; } - - foreach(var product in products) + + foreach (var product in products) { product.Name = null; } @@ -114,7 +116,7 @@ protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option { product.Name = x.Products.Rows[y => !products.Any(p => p.Name == y.Name.Value) && product.HasAmountDiscount == !string.IsNullOrWhiteSpace(y.AmountDiscount.Value)].Name.Value; } - else if(product.HasPercentDiscount) + else if (product.HasPercentDiscount) { product.Name = x.Products.Rows[y => !products.Any(p => p.Name == y.Name.Value) && product.HasPercentDiscount == !string.IsNullOrWhiteSpace(y.PercentDiscount.Value)].Name.Value; } @@ -122,7 +124,7 @@ protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option { product.Name = x.Products.Rows[y => !products.Any(p => p.Name == y.Name.Value) && product.HasAmountDiscount == !string.IsNullOrWhiteSpace(y.AmountDiscount.Value) && product.HasPercentDiscount == !string.IsNullOrWhiteSpace(y.PercentDiscount.Value)].Name.Value; } - + x .Products.Rows[y => y.Name.Value == product.Name].AddToCart.ClickAndGo() .Products.Rows[y => y.Name.Value == product.Name].Price.StoreNumericalValue(out var price, characterToRemove: " ") @@ -141,8 +143,8 @@ protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option product.Currency = currency; double discount = 0; - - if(!string.IsNullOrEmpty(amountDiscount.ToString()) && amountDiscount != 0) + + if (!string.IsNullOrEmpty(amountDiscount.ToString()) && amountDiscount != 0) { discount = double.Parse(amountDiscount.ToString()); } @@ -158,7 +160,12 @@ protected ProductsPage SelectProducts(Product[] products, PaymentMethods.Option }); } - protected SveaPaymentFramePage GoToSveaPaymentFrame(Product[] products, bool requireBankId = false, bool isInternational = false, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) + protected SveaPaymentFramePage GoToSveaPaymentFrame( + Product[] products, + bool requireBankId = false, + bool isInternational = false, + Dictionary shipping = null, + PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) { Frame frame; @@ -174,7 +181,13 @@ protected SveaPaymentFramePage GoToSveaPaymentFrame(Product[] products, bool req .InternationalCheckout.ClickAndGo() .SveaFrame; } - else + else if (shipping != null) + { + frame = SelectProducts(products, paymentMethod) + .ShippingCheckout.ClickAndGo() + .SveaFrame; + } + else { frame = SelectProducts(products, paymentMethod) .AnonymousCheckout.ClickAndGo() @@ -188,9 +201,13 @@ protected SveaPaymentFramePage GoToSveaPaymentFrame(Product[] products, bool req return frame.SwitchTo(); ; } - protected SveaPaymentFramePage GoToBankId(Product[] products, Checkout.Option checkout = Checkout.Option.Identification, Entity.Option entity = Entity.Option.Private, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) + protected SveaPaymentFramePage GoToBankId( + Product[] products, + Checkout.Option checkout = Checkout.Option.Identification, + Entity.Option entity = Entity.Option.Private, + PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card) { - var page = GoToSveaPaymentFrame(products, requireBankId: true, isInternational: false, paymentMethod); + var page = GoToSveaPaymentFrame(products, requireBankId: true, isInternational: false, null, paymentMethod); try { @@ -203,7 +220,7 @@ protected SveaPaymentFramePage GoToBankId(Product[] products, Checkout.Option ch .IdentifyEntity(checkout, entity, paymentMethod); } - switch(paymentMethod) + switch (paymentMethod) { case PaymentMethods.Option.Invoice: page @@ -236,21 +253,32 @@ protected SveaPaymentFramePage GoToBankId(Product[] products, Checkout.Option ch return page; } - protected ThankYouPage GoToThankYouPage(Product[] products, Checkout.Option checkout = Checkout.Option.Identification, Entity.Option entity = Entity.Option.Private, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card, bool requireBankId = false) + protected ThankYouPage GoToThankYouPage( + Product[] products, + Checkout.Option checkout = Checkout.Option.Identification, + Entity.Option entity = Entity.Option.Private, + Dictionary shipping = null, + PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card, + bool requireBankId = false) { - var page = GoToSveaPaymentFrame(products, requireBankId: requireBankId, isInternational: false, paymentMethod); + var page = GoToSveaPaymentFrame(products, requireBankId, isInternational: false, shipping, paymentMethod); try { - page.IdentifyEntity(checkout, entity, paymentMethod); + page.IdentifyEntity(checkout, entity, paymentMethod, shipping); } - catch(StaleElementReferenceException) + catch (StaleElementReferenceException) { page.RefreshPage() .SwitchToFrame(By.Id("svea-checkout-iframe")) .IdentifyEntity(checkout, entity, paymentMethod); } - + + if (shipping != null) + { + page.EditShipping(shipping); + } + page.Pay(checkout, entity, paymentMethod, _amountStr); try @@ -266,10 +294,16 @@ protected ThankYouPage GoToThankYouPage(Product[] products, Checkout.Option chec } } - protected OrdersPage GoToOrdersPage(Product[] products, Checkout.Option checkout = Checkout.Option.Identification, Entity.Option entity = Entity.Option.Private, PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card, bool requireBankId = false) + protected OrdersPage GoToOrdersPage( + Product[] products, + Checkout.Option checkout = Checkout.Option.Identification, + Entity.Option entity = Entity.Option.Private, + PaymentMethods.Option paymentMethod = PaymentMethods.Option.Card, + Dictionary shipping = null, + bool requireBankId = false) { - return GoToThankYouPage(products, checkout, entity, paymentMethod, requireBankId) - .Do(x => + return GoToThankYouPage(products, checkout, entity, shipping, paymentMethod, requireBankId) + .Do(x => { switch (paymentMethod) { @@ -292,7 +326,11 @@ protected OrdersPage GoToOrdersPage(Product[] products, Checkout.Option checkout .Header.Orders.ClickAndGo(); } - protected static IEnumerable TestData(bool singleProduct = true, bool hasAmountDiscount = false, bool hasPercentDiscount = false, bool manySameArticle = false) + protected static IEnumerable TestData( + bool singleProduct = true, + bool hasAmountDiscount = false, + bool hasPercentDiscount = false, + bool manySameArticle = false) { var data = new List(); @@ -303,13 +341,13 @@ protected static IEnumerable TestData(bool singleProduct = true, bool hasAmountD new Product { Quantity = 1, HasAmountDiscount = hasAmountDiscount, HasPercentDiscount = hasPercentDiscount } }); } - else if(manySameArticle) + else if (manySameArticle) { data.Add(new[] { new Product { Quantity = 8, HasAmountDiscount = hasAmountDiscount, HasPercentDiscount = hasPercentDiscount } }); - } + } else data.Add(new[] { diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/LeasingOrderTests.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/LeasingOrderTests.cs index 94aee46a..e128c542 100644 --- a/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/LeasingOrderTests.cs +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/LeasingOrderTests.cs @@ -19,7 +19,7 @@ public LeasingOrderTests(string driverAlias) [RetryWithException(2)] [Test(Description = "")] [TestCaseSource(nameof(TestData), new object[] { false, false, false, true })] - public void CreditWithVippsAsPrivateAsync(Product[] products) + public void CreditWithLeasingAsCompanyAsync(Product[] products) { Assert.DoesNotThrowAsync(async () => { diff --git a/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/ShippingOrderTests.cs b/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/ShippingOrderTests.cs new file mode 100644 index 00000000..00f35a64 --- /dev/null +++ b/src/Samples/Sample.AspNetCore.SystemTests/Test/PaymentTests/Payment/ShippingOrderTests.cs @@ -0,0 +1,369 @@ +using Atata; +using NUnit.Framework; +using Sample.AspNetCore.SystemTests.Services; +using Sample.AspNetCore.SystemTests.Test.Base; +using Sample.AspNetCore.SystemTests.Test.Helpers; +using Svea.WebPay.SDK.CheckoutApi; +using Svea.WebPay.SDK.PaymentAdminApi; +using System; +using System.Collections.Generic; +using System.Linq; +using Checkout = Sample.AspNetCore.SystemTests.Test.Helpers.Checkout; + +namespace Sample.AspNetCore.SystemTests.Test.PaymentTests.Payment +{ + public class ShippingOrderTests : Base.PaymentTests + { + public ShippingOrderTests(string driverAlias) + : base(driverAlias) + { + } + + [RetryWithException(2)] + [Test(Description = "8226 - As a user I want to finalize a card purchase with the first shipping option")] + [TestCaseSource(nameof(TestData), new object[] { true, false, false, false })] + public void OrderWithShippingOptionWithCard(Product[] products) + { + var shipping = new Dictionary + { + { "carrier", new string[] { ShippingOptions.Plab } } + }; + + Assert.DoesNotThrowAsync(async () => + { + GoToOrdersPage(products, Checkout.Option.Identification, Entity.Option.Private, PaymentMethods.Option.Card, shipping, false) + + .RefreshPageUntil(x => + x.PageUri.Value.AbsoluteUri.Contains("Orders/Details") && + x.Details.Exists(new SearchOptions { IsSafely = true, Timeout = TimeSpan.FromSeconds(1) }) && + x.Orders.Count() > 0, 15, 3) + + .Orders.Last().Order.OrderId.StoreValue(out var orderId) + + // Validate order info + .Orders.Last().Order.OrderStatus.Should.Equal(nameof(OrderStatus.Open)) + .Orders.Last().Order.PaymentType.Should.Equal(nameof(Svea.WebPay.SDK.PaymentAdminApi.PaymentType.Card)) + .Orders.Last().Order.ShippingStatus.Should.Contain("shipping.created") + .Orders.Last().Order.ShippingDescription.Should.Contain(ShippingOptions.Plab) + + // Validate deliveries info + .Orders.Last().Deliveries.Should.HaveCount(0) + .Orders.Last().Order.Table.Toggle.Click() + .Orders.Last().Order.Table.DeliverOrder.ClickAndGo(); + + // Assert sdk/api response + var response = await _sveaClientSweden.Checkout.GetOrder(long.Parse(orderId)).ConfigureAwait(false); + + var shippingOption = response.ShippingInformation.ShippingProvider.ShippingOption; + + Assert.That(response.Currency, Is.EqualTo("SEK")); + Assert.That(response.EmailAddress.ToString(), Is.EqualTo(TestDataService.Email)); + Assert.That(response.PaymentType.ToString(), Is.EqualTo(nameof(Svea.WebPay.SDK.CheckoutApi.PaymentType.SVEACARDPAY))); + Assert.That(response.Status.ToString(), Is.EqualTo(nameof(CheckoutOrderStatus.Final))); + + Assert.That(response.ShippingAddress.FirstName, Is.EqualTo(TestDataService.SwedishFirstName)); + Assert.That(response.ShippingAddress.LastName, Is.EqualTo(TestDataService.SwedishLastName)); + Assert.That(response.ShippingAddress.StreetAddress, Is.EqualTo(TestDataService.ShippingStreetAddress)); + Assert.That(response.ShippingAddress.PostalCode, Is.EqualTo(TestDataService.ShippingZipCode.Replace(" ", ""))); + Assert.That(response.ShippingAddress.City.ToLower(), Is.EqualTo(TestDataService.ShippingCity.ToLower())); + + Assert.That(shippingOption.Carrier.ToLower(), Is.EqualTo(nameof(ShippingOptions.Plab).ToLower())); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCDELIVERYINSTRUCTIONS").Value, Is.EqualTo(TestDataService.ShippingInstructions)); + Assert.That(shippingOption.Addons.First(x => x.Id == "FCNOTIFYPHONE").Fields.First().Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Addons.First(x => x.Id == "FCNOTIFYPHONE").Fields.First().Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Addons.First(x => x.Id == "FCDELIVERYTIME12"), Is.Not.Null); + }); + } + + [RetryWithException(2)] + [Test(Description = "8227 - As a user I want to finalize a card purchase with the first shipping option, but change the place of delivery to the second option")] + [TestCaseSource(nameof(TestData), new object[] { true, false, false, false })] + public void OrderWithShippingOptionAndChangePickupOptionWithCard(Product[] products) + { + var shipping = new Dictionary + { + { "carrier", new string[] { ShippingOptions.Plab, ShippingOptions.Bring } }, + { "pickup", new string[] { 1.ToString() } }, + }; + + Assert.DoesNotThrowAsync(async () => + { + GoToOrdersPage(products, Checkout.Option.Identification, Entity.Option.Private, PaymentMethods.Option.Card, shipping, false) + + .RefreshPageUntil(x => + x.PageUri.Value.AbsoluteUri.Contains("Orders/Details") && + x.Details.Exists(new SearchOptions { IsSafely = true, Timeout = TimeSpan.FromSeconds(1) }) && + x.Orders.Count() > 0, 15, 3) + + .Orders.Last().Order.OrderId.StoreValue(out var orderId) + + // Validate order info + .Orders.Last().Order.OrderStatus.Should.Equal(nameof(OrderStatus.Open)) + .Orders.Last().Order.PaymentType.Should.Equal(nameof(Svea.WebPay.SDK.PaymentAdminApi.PaymentType.Card)) + .Orders.Last().Order.ShippingStatus.Should.Contain("shipping.created") + .Orders.Last().Order.ShippingDescription.Should.Contain(ShippingOptions.Bring) + + // Validate deliveries info + .Orders.Last().Deliveries.Should.HaveCount(0) + .Orders.Last().Order.Table.Toggle.Click() + .Orders.Last().Order.Table.DeliverOrder.ClickAndGo(); + + // Assert sdk/api response + var response = await _sveaClientSweden.Checkout.GetOrder(long.Parse(orderId)).ConfigureAwait(false); + + var shippingOption = response.ShippingInformation.ShippingProvider.ShippingOption; + + Assert.That(response.Currency, Is.EqualTo("SEK")); + Assert.That(response.EmailAddress.ToString(), Is.EqualTo(TestDataService.Email)); + Assert.That(response.PaymentType.ToString(), Is.EqualTo(nameof(Svea.WebPay.SDK.CheckoutApi.PaymentType.SVEACARDPAY))); + Assert.That(response.Status.ToString(), Is.EqualTo(nameof(CheckoutOrderStatus.Final))); + + Assert.That(response.ShippingAddress.FirstName, Is.EqualTo(TestDataService.SwedishFirstName)); + Assert.That(response.ShippingAddress.LastName, Is.EqualTo(TestDataService.SwedishLastName)); + Assert.That(response.ShippingAddress.StreetAddress, Is.EqualTo(TestDataService.ShippingStreetAddress)); + Assert.That(response.ShippingAddress.PostalCode, Is.EqualTo(TestDataService.ShippingZipCode.Replace(" ", ""))); + Assert.That(response.ShippingAddress.City.ToLower(), Is.EqualTo(TestDataService.ShippingCity.ToLower())); + + Assert.That(shippingOption.Carrier.ToLower(), Is.EqualTo(nameof(ShippingOptions.Bring).ToLower())); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERDOORCODE").Value, Is.EqualTo(TestDataService.DoorCode)); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCDELIVERYINSTRUCTIONS").Value, Is.EqualTo(TestDataService.ShippingInstructions)); + }); + } + + [RetryWithException(2)] + [Test(Description = "8228 - As a user I want to finalize a card purchase with the first shipping option, but change the place of delivery to the last option")] + [TestCaseSource(nameof(TestData), new object[] { true, false, false, false })] + public void OrderWithShippingOptionAndChangeToLastPickupOptionWithCard(Product[] products) + { + var shipping = new Dictionary + { + { "carrier", new string[] { ShippingOptions.Plab, ShippingOptions.Bring } }, + { "pickup", new string[] { (-1).ToString() } }, + }; + + Assert.DoesNotThrowAsync(async () => + { + GoToOrdersPage(products, Checkout.Option.Identification, Entity.Option.Private, PaymentMethods.Option.Card, shipping, false) + + .RefreshPageUntil(x => + x.PageUri.Value.AbsoluteUri.Contains("Orders/Details") && + x.Details.Exists(new SearchOptions { IsSafely = true, Timeout = TimeSpan.FromSeconds(1) }) && + x.Orders.Count() > 0, 15, 3) + + .Orders.Last().Order.OrderId.StoreValue(out var orderId) + + // Validate order info + .Orders.Last().Order.OrderStatus.Should.Equal(nameof(OrderStatus.Open)) + .Orders.Last().Order.PaymentType.Should.Equal(nameof(Svea.WebPay.SDK.PaymentAdminApi.PaymentType.Card)) + .Orders.Last().Order.ShippingStatus.Should.Contain("shipping.created") + .Orders.Last().Order.ShippingDescription.Should.Contain(ShippingOptions.Bring) + + // Validate deliveries info + .Orders.Last().Deliveries.Should.HaveCount(0) + .Orders.Last().Order.Table.Toggle.Click() + .Orders.Last().Order.Table.DeliverOrder.ClickAndGo(); + + // Assert sdk/api response + var response = await _sveaClientSweden.Checkout.GetOrder(long.Parse(orderId)).ConfigureAwait(false); + + var shippingOption = response.ShippingInformation.ShippingProvider.ShippingOption; + + Assert.That(response.Currency, Is.EqualTo("SEK")); + Assert.That(response.EmailAddress.ToString(), Is.EqualTo(TestDataService.Email)); + Assert.That(response.PaymentType.ToString(), Is.EqualTo(nameof(Svea.WebPay.SDK.CheckoutApi.PaymentType.SVEACARDPAY))); + Assert.That(response.Status.ToString(), Is.EqualTo(nameof(CheckoutOrderStatus.Final))); + + Assert.That(response.ShippingAddress.FirstName, Is.EqualTo(TestDataService.SwedishFirstName)); + Assert.That(response.ShippingAddress.LastName, Is.EqualTo(TestDataService.SwedishLastName)); + Assert.That(response.ShippingAddress.StreetAddress, Is.EqualTo(TestDataService.ShippingStreetAddress)); + Assert.That(response.ShippingAddress.PostalCode, Is.EqualTo(TestDataService.ShippingZipCode.Replace(" ", ""))); + Assert.That(response.ShippingAddress.City.ToLower(), Is.EqualTo(TestDataService.ShippingCity.ToLower())); + + Assert.That(shippingOption.Carrier.ToLower(), Is.EqualTo(nameof(ShippingOptions.Bring).ToLower())); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERDOORCODE").Value, Is.EqualTo(TestDataService.DoorCode)); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCDELIVERYINSTRUCTIONS").Value, Is.EqualTo(TestDataService.ShippingInstructions)); + Assert.That(shippingOption.Addons.Count(), Is.EqualTo(0)); + }); + } + + [RetryWithException(2)] + [Test(Description = "8229 - As a user I want to finalize an invoice purchase with the second shipping option")] + [TestCaseSource(nameof(TestData), new object[] { true, false, false, false })] + public void OrderWithShippingOptionWithInvoice(Product[] products) + { + var shipping = new Dictionary + { + { "carrier", new string[] { ShippingOptions.Plab, ShippingOptions.Bring } } + }; + + Assert.DoesNotThrowAsync(async () => + { + GoToOrdersPage(products, Checkout.Option.Identification, Entity.Option.Private, PaymentMethods.Option.Invoice, shipping, false) + + .RefreshPageUntil(x => + x.PageUri.Value.AbsoluteUri.Contains("Orders/Details") && + x.Details.Exists(new SearchOptions { IsSafely = true, Timeout = TimeSpan.FromSeconds(1) }) && + x.Orders.Count() > 0, 15, 3) + + .Orders.Last().Order.OrderId.StoreValue(out var orderId) + + // Validate order info + .Orders.Last().Order.OrderStatus.Should.Equal(nameof(OrderStatus.Open)) + .Orders.Last().Order.PaymentType.Should.Equal(nameof(Svea.WebPay.SDK.PaymentAdminApi.PaymentType.Invoice)) + .Orders.Last().Order.ShippingStatus.Should.Contain("shipping.created") + .Orders.Last().Order.ShippingDescription.Should.Contain(ShippingOptions.Bring) + + // Validate deliveries info + .Orders.Last().Deliveries.Should.HaveCount(0) + .Orders.Last().Order.Table.Toggle.Click() + .Orders.Last().Order.Table.DeliverOrder.ClickAndGo(); + + // Assert sdk/api response + var response = await _sveaClientSweden.Checkout.GetOrder(long.Parse(orderId)).ConfigureAwait(false); + + var shippingOption = response.ShippingInformation.ShippingProvider.ShippingOption; + + Assert.That(response.Currency, Is.EqualTo("SEK")); + Assert.That(response.EmailAddress.ToString(), Is.EqualTo(TestDataService.Email)); + Assert.That(response.PaymentType.ToString(), Is.EqualTo(nameof(Svea.WebPay.SDK.CheckoutApi.PaymentType.INVOICE))); + Assert.That(response.Status.ToString(), Is.EqualTo(nameof(CheckoutOrderStatus.Final))); + + Assert.That(response.ShippingAddress.FirstName, Is.EqualTo(TestDataService.SwedishFirstName)); + Assert.That(response.ShippingAddress.LastName, Is.EqualTo(TestDataService.SwedishLastName)); + Assert.That(response.ShippingAddress.StreetAddress, Is.EqualTo(TestDataService.ShippingStreetAddress)); + Assert.That(response.ShippingAddress.PostalCode, Is.EqualTo(TestDataService.ShippingZipCode.Replace(" ", ""))); + Assert.That(response.ShippingAddress.City.ToLower(), Is.EqualTo(TestDataService.ShippingCity.ToLower())); + + Assert.That(shippingOption.Carrier.ToLower(), Is.EqualTo(nameof(ShippingOptions.Bring).ToLower())); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERDOORCODE").Value, Is.EqualTo(TestDataService.DoorCode)); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCDELIVERYINSTRUCTIONS").Value, Is.EqualTo(TestDataService.ShippingInstructions)); + Assert.That(shippingOption.Addons.Count(), Is.EqualTo(0)); + }); + } + + [RetryWithException(2)] + [Test(Description = "8230 - As a user I want to finalize an invoice purchase with the last shipping option")] + [TestCaseSource(nameof(TestData), new object[] { true, false, false, false })] + public void OrderWithLastShippingOptionWithInvoice(Product[] products) + { + var shipping = new Dictionary + { + { "carrier", new string[] { ShippingOptions.Plab, ShippingOptions.Budbee } } + }; + + Assert.DoesNotThrowAsync(async () => + { + GoToOrdersPage(products, Checkout.Option.Identification, Entity.Option.Private, PaymentMethods.Option.Invoice, shipping, false) + + .RefreshPageUntil(x => + x.PageUri.Value.AbsoluteUri.Contains("Orders/Details") && + x.Details.Exists(new SearchOptions { IsSafely = true, Timeout = TimeSpan.FromSeconds(1) }) && + x.Orders.Count() > 0, 15, 3) + + .Orders.Last().Order.OrderId.StoreValue(out var orderId) + + // Validate order info + .Orders.Last().Order.OrderStatus.Should.Equal(nameof(OrderStatus.Open)) + .Orders.Last().Order.PaymentType.Should.Equal(nameof(Svea.WebPay.SDK.PaymentAdminApi.PaymentType.Invoice)) + .Orders.Last().Order.ShippingStatus.Should.Contain("shipping.created") + .Orders.Last().Order.ShippingDescription.Should.Contain(ShippingOptions.Budbee) + + // Validate deliveries info + .Orders.Last().Deliveries.Should.HaveCount(0) + .Orders.Last().Order.Table.Toggle.Click() + .Orders.Last().Order.Table.DeliverOrder.ClickAndGo(); + + // Assert sdk/api response + var response = await _sveaClientSweden.Checkout.GetOrder(long.Parse(orderId)).ConfigureAwait(false); + + var shippingOption = response.ShippingInformation.ShippingProvider.ShippingOption; + + Assert.That(response.Currency, Is.EqualTo("SEK")); + Assert.That(response.EmailAddress.ToString(), Is.EqualTo(TestDataService.Email)); + Assert.That(response.PaymentType.ToString(), Is.EqualTo(nameof(Svea.WebPay.SDK.CheckoutApi.PaymentType.INVOICE))); + Assert.That(response.Status.ToString(), Is.EqualTo(nameof(CheckoutOrderStatus.Final))); + + Assert.That(response.ShippingAddress.FirstName, Is.EqualTo(TestDataService.SwedishFirstName)); + Assert.That(response.ShippingAddress.LastName, Is.EqualTo(TestDataService.SwedishLastName)); + Assert.That(response.ShippingAddress.StreetAddress, Is.EqualTo(TestDataService.ShippingStreetAddress)); + Assert.That(response.ShippingAddress.PostalCode, Is.EqualTo(TestDataService.ShippingZipCode.Replace(" ", ""))); + Assert.That(response.ShippingAddress.City.ToLower(), Is.EqualTo(TestDataService.ShippingCity.ToLower())); + + Assert.That(shippingOption.Carrier.ToLower().Contains(nameof(ShippingOptions.Budbee).ToLower()), Is.True); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERPHONE").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERPHONE").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCDELIVERYINSTRUCTIONS").Value, Is.EqualTo(TestDataService.ShippingInstructions)); + Assert.That(shippingOption.Addons.Count(), Is.EqualTo(0)); + }); + } + + [RetryWithException(2)] + [Test(Description = "8231 - As a user I want to finalize an invoice purchase with the last shipping option, and change place of delivery to the last option")] + [TestCaseSource(nameof(TestData), new object[] { true, false, false, false })] + public void OrderWithLastShippingOptionAndLastPickupOptionWithInvoice(Product[] products) + { + var shipping = new Dictionary + { + { "carrier", new string[] { ShippingOptions.Plab, ShippingOptions.Dhl } }, + { "pickup", new string[] { (-1).ToString() } }, + }; + + Assert.DoesNotThrowAsync(async () => + { + GoToOrdersPage(products, Checkout.Option.Identification, Entity.Option.Private, PaymentMethods.Option.Invoice, shipping, false) + + .RefreshPageUntil(x => + x.PageUri.Value.AbsoluteUri.Contains("Orders/Details") && + x.Details.Exists(new SearchOptions { IsSafely = true, Timeout = TimeSpan.FromSeconds(1) }) && + x.Orders.Count() > 0, 15, 3) + + .Orders.Last().Order.OrderId.StoreValue(out var orderId) + + // Validate order info + .Orders.Last().Order.OrderStatus.Should.Equal(nameof(OrderStatus.Open)) + .Orders.Last().Order.PaymentType.Should.Equal(nameof(Svea.WebPay.SDK.PaymentAdminApi.PaymentType.Invoice)) + .Orders.Last().Order.ShippingStatus.Should.Contain("shipping.created") + .Orders.Last().Order.ShippingDescription.Should.Contain(ShippingOptions.Dhl) + + // Validate deliveries info + .Orders.Last().Deliveries.Should.HaveCount(0) + .Orders.Last().Order.Table.Toggle.Click() + .Orders.Last().Order.Table.DeliverOrder.ClickAndGo(); + + // Assert sdk/api response + var response = await _sveaClientSweden.Checkout.GetOrder(long.Parse(orderId)).ConfigureAwait(false); + + var shippingOption = response.ShippingInformation.ShippingProvider.ShippingOption; + + Assert.That(response.Currency, Is.EqualTo("SEK")); + Assert.That(response.EmailAddress.ToString(), Is.EqualTo(TestDataService.Email)); + Assert.That(response.PaymentType.ToString(), Is.EqualTo(nameof(Svea.WebPay.SDK.CheckoutApi.PaymentType.INVOICE))); + Assert.That(response.Status.ToString(), Is.EqualTo(nameof(CheckoutOrderStatus.Final))); + + Assert.That(response.ShippingAddress.FirstName, Is.EqualTo(TestDataService.SwedishFirstName)); + Assert.That(response.ShippingAddress.LastName, Is.EqualTo(TestDataService.SwedishLastName)); + Assert.That(response.ShippingAddress.StreetAddress, Is.EqualTo(TestDataService.ShippingStreetAddress)); + Assert.That(response.ShippingAddress.PostalCode, Is.EqualTo(TestDataService.ShippingZipCode.Replace(" ", ""))); + Assert.That(response.ShippingAddress.City.ToLower(), Is.EqualTo(TestDataService.ShippingCity.ToLower())); + + Assert.That(shippingOption.Carrier.ToLower().Contains(nameof(ShippingOptions.Dhl).ToLower()), Is.True); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERPHONE").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERPHONE").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Take(3), Is.EqualTo(TestDataService.SwedishPhoneNumber.Take(3))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERSMS").Value.Skip(9), Is.EqualTo(TestDataService.SwedishPhoneNumber.Skip(9))); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCRECEIVERDOORCODE").Value, Is.EqualTo(TestDataService.DoorCode)); + Assert.That(shippingOption.Fields.First(x => x.Id == "FCDELIVERYINSTRUCTIONS").Value, Is.EqualTo(TestDataService.ShippingInstructions)); + Assert.That(shippingOption.Addons.Count(), Is.EqualTo(0)); + }); + } + + } +} diff --git a/src/Samples/Sample.AspNetCore/Controllers/CheckOutController.cs b/src/Samples/Sample.AspNetCore/Controllers/CheckOutController.cs index 398cb11c..4846f574 100644 --- a/src/Samples/Sample.AspNetCore/Controllers/CheckOutController.cs +++ b/src/Samples/Sample.AspNetCore/Controllers/CheckOutController.cs @@ -2,92 +2,98 @@ using Microsoft.Extensions.Options; using Sample.AspNetCore.Extensions; using Sample.AspNetCore.Models; +using Svea.WebPay.SDK; +using Svea.WebPay.SDK.CheckoutApi; +using Svea.WebPay.SDK.Exceptions; using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Threading.Tasks; -using Svea.WebPay.SDK; -using Svea.WebPay.SDK.CheckoutApi; using Cart = Sample.AspNetCore.Models.Cart; -namespace Sample.AspNetCore.Controllers +namespace Sample.AspNetCore.Controllers; + +using System.Data.SqlTypes; + +public class CheckOutController : Controller { - using Svea.WebPay.SDK.Exceptions; - using System.Globalization; + private readonly Cart _cartService; + private readonly Market _marketService; + private readonly SveaWebPayClient _sveaClient; + private readonly Models.MerchantSettings _merchantSettings; + + + public CheckOutController( + IOptionsSnapshot merchantsAccessor, + Cart cartService, + Market marketService, + SveaWebPayClient sveaClient) + { + _merchantSettings = merchantsAccessor.Value; + _cartService = cartService; + _marketService = marketService; + _sveaClient = sveaClient; + } + - public class CheckOutController : Controller + public async Task LoadPaymentMenu(bool requireBankId, bool isInternational, bool enableShipping = false) { - private readonly Cart _cartService; - private readonly Market _marketService; - private readonly SveaWebPayClient _sveaClient; - private readonly Models.MerchantSettings _merchantSettings; + var data = await CreatePaymentOrder(requireBankId, isInternational, enableShipping); + var snippet = data.Gui.Snippet; - public CheckOutController( - IOptionsSnapshot merchantsAccessor, - Cart cartService, - Market marketService, - SveaWebPayClient sveaClient) + var sveaCheckoutSource = new SveaCheckoutSource { - _merchantSettings = merchantsAccessor.Value; - _cartService = cartService; - _marketService = marketService; - _sveaClient = sveaClient; - } + Snippet = snippet + }; + return View("Checkout", sveaCheckoutSource); + } - public async Task LoadPaymentMenu(bool requireBankId, bool isInternational) + public async Task CreatePaymentOrder(bool requireBanKId = false, bool isInternational = false, bool enableShipping = false) + { + var orderItems = _cartService.CartLines.ToOrderItems().ToList(); + try { - var data = await CreatePaymentOrder(requireBankId, isInternational); + var currencyRequest = new CurrencyCode(_marketService.CurrencyCode); + var languageRequest = new Language(_marketService.LanguageId); + var regionRequest = new RegionInfo(_marketService.MarketId); - var snippet = data.Gui.Snippet; + var region = isInternational ? new RegionInfo(_marketService.CountryId) : regionRequest; - var SveaCheckoutSource = new SveaCheckoutSource - { - Snippet = snippet - }; + var pushUri = new Uri(_merchantSettings.PushUri.ToString().Replace("{marketId}", _marketService.MarketId)); + var shippingCallbackUri = new Uri(_merchantSettings.WebhookUri.ToString().Replace("{marketId}", _marketService.MarketId)); - return View("Checkout", SveaCheckoutSource); - } + var checkoutValidationCallbackUri = new Uri(_merchantSettings.CheckoutValidationCallbackUri.ToString().Replace("{marketId}", _marketService.MarketId)); + + var shippingFallbacks = new List { new FallbackOption("79d0c2d3-71f4-4205-a5bc-4aa9ab324c98", "DHL Home Delivery", "dhl", Convert.ToInt64(_cartService.CalculateTotal()), null, null) }; + var shippingInformation = new ShippingInformation(true, 1000, null, null); + + var paymentOrderRequest = new CreateOrderModel(region, currencyRequest, languageRequest, DateTime.Now.Ticks.ToString(), + new Svea.WebPay.SDK.CheckoutApi.MerchantSettings(pushUri, _merchantSettings.TermsUri, _merchantSettings.CheckoutUri, _merchantSettings.ConfirmationUri, checkoutValidationCallbackUri, shippingCallbackUri), + new Svea.WebPay.SDK.CheckoutApi.Cart(orderItems), requireBanKId, null, null, null, null, enableShipping ? shippingInformation : null); + + var data = await _sveaClient.Checkout.CreateOrder(paymentOrderRequest).ConfigureAwait(false); - public async Task CreatePaymentOrder(bool requireBanKId = false, bool isInternational = false) + return data; + } + catch (HttpResponseException ex) { - var orderItems = _cartService.CartLines.ToOrderItems().ToList(); - try - { - var currencyRequest = new CurrencyCode(_marketService.CurrencyCode); - var languageRequest = new Language(_marketService.LanguageId); - var regionRequest = new RegionInfo(_marketService.MarketId); - - var region = isInternational ? new RegionInfo(_marketService.CountryId) : regionRequest; - - var pushUri = new Uri(_merchantSettings.PushUri.ToString().Replace("{marketId}", _marketService.MarketId)); - var checkoutValidationCallbackUri = new Uri(_merchantSettings.CheckoutValidationCallbackUri.ToString().Replace("{marketId}", _marketService.MarketId)); - - var paymentOrderRequest = new CreateOrderModel(region, currencyRequest, languageRequest, DateTime.Now.Ticks.ToString(), - new Svea.WebPay.SDK.CheckoutApi.MerchantSettings(pushUri, _merchantSettings.TermsUri, _merchantSettings.CheckoutUri, _merchantSettings.ConfirmationUri, checkoutValidationCallbackUri), - new Svea.WebPay.SDK.CheckoutApi.Cart(orderItems), requireBanKId); - - var data = await _sveaClient.Checkout.CreateOrder(paymentOrderRequest).ConfigureAwait(false); - - return data; - } - catch (HttpResponseException ex) - { - Debug.Write(ex.Message); - return null; - } - catch (Exception ex) - { - Debug.Write(ex.Message); - return null; - } + Debug.Write(ex.Message); + return null; } - - public ViewResult Thankyou() + catch (Exception ex) { - _cartService.Clear(); - return View(); + Debug.Write(ex.Message); + return null; } } + + public ViewResult Thankyou() + { + _cartService.Clear(); + return View(); + } } \ No newline at end of file diff --git a/src/Samples/Sample.AspNetCore/Controllers/OrdersController.cs b/src/Samples/Sample.AspNetCore/Controllers/OrdersController.cs index eb270c14..f06fc9b2 100644 --- a/src/Samples/Sample.AspNetCore/Controllers/OrdersController.cs +++ b/src/Samples/Sample.AspNetCore/Controllers/OrdersController.cs @@ -98,6 +98,8 @@ public async Task Details(int? id) { orderViewModel.Order = await this._sveaClient.PaymentAdmin.GetOrder(long.Parse(order.SveaOrderId)).ConfigureAwait(false); orderViewModel.IsLoaded = true; + orderViewModel.ShippingStatus = order.ShippingStatus; + orderViewModel.ShippingDescription = order.ShippingDescription; } catch {} diff --git a/src/Samples/Sample.AspNetCore/Controllers/SveaController.cs b/src/Samples/Sample.AspNetCore/Controllers/SveaController.cs index f8dea643..fb40d55e 100644 --- a/src/Samples/Sample.AspNetCore/Controllers/SveaController.cs +++ b/src/Samples/Sample.AspNetCore/Controllers/SveaController.cs @@ -5,12 +5,13 @@ namespace Sample.AspNetCore.Controllers { using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; + using Sample.AspNetCore.Data; using Sample.AspNetCore.Models; using Svea.WebPay.SDK; using Svea.WebPay.SDK.CheckoutApi; - + using Svea.WebPay.SDK.CheckoutApi.Response; using System; using Cart = Models.Cart; @@ -33,6 +34,41 @@ public SveaController(ILogger logger, Cart cartService, StoreDbC _sveaClient = sveaClient; } + + [HttpPost("shippingTaxCalculation")] + public async Task ShippingTaxCalculation(ShippingOption shippingOption) + { + var order = await _sveaClient.Checkout.GetOrder(shippingOption.OrderId).ConfigureAwait(false); + order.Cart.CalculateShippingOrderRows(shippingOption); + + await _sveaClient.Checkout.UpdateOrder(order.OrderId, new UpdateOrderModel(order.Cart, null, order.ShippingInformation)).ConfigureAwait(false); + + return Ok(); + } + + [HttpPost("shippingvalidation")] + public async Task ShippingValidation(ShippingCallbackResponse shippingCallbackResponse) + { + var order = await _sveaClient.Checkout.GetOrder(shippingCallbackResponse.OrderId).ConfigureAwait(false); + + if (order != null && order.Status == CheckoutOrderStatus.Final) + { + var existingOrder = _context.Orders.FirstOrDefault(x => x.SveaOrderId == order.OrderId.ToString()); + if (existingOrder == null) + { + return Problem(); + } + + existingOrder.ShippingStatus = shippingCallbackResponse.Type; + existingOrder.ShippingDescription = shippingCallbackResponse.Description; + + await _context.SaveChangesAsync(true); + } + + return Ok(); + } + + [HttpGet("validation/{orderId}")] public ActionResult Validation(long? orderId) { @@ -48,6 +84,7 @@ public async Task Push(long? orderId) if (orderId.HasValue) { var order = await _sveaClient.Checkout.GetOrder(orderId.Value).ConfigureAwait(false); + if (order != null && order.Status == CheckoutOrderStatus.Final) { _cartService.SveaOrderId = order.OrderId.ToString(); @@ -66,9 +103,11 @@ public async Task Push(long? orderId) _context.Orders.Add(new Order { SveaOrderId = _cartService.SveaOrderId, - Lines = _cartService.CartLines.ToList() + Lines = _cartService.CartLines.ToList(), + ShippingStatus = _cartService.ShippingStatus }); - _context.SaveChanges(true); + + await _context.SaveChangesAsync(true); } } @@ -76,7 +115,7 @@ public async Task Push(long? orderId) } catch (Exception e) { - _logger.LogError( e, "Callback failed"); + _logger.LogError(e, "Callback failed"); return Ok(); } } diff --git a/src/Samples/Sample.AspNetCore/Models/Cart.cs b/src/Samples/Sample.AspNetCore/Models/Cart.cs index db2d5380..c0e0155c 100644 --- a/src/Samples/Sample.AspNetCore/Models/Cart.cs +++ b/src/Samples/Sample.AspNetCore/Models/Cart.cs @@ -20,6 +20,8 @@ public virtual IEnumerable CartLines public string SveaOrderId { get; set; } public bool Vat { get; set; } public bool IsInternational { get; set; } + public string ShippingStatus { get; set; } + public string ShippingDescription { get; set; } public virtual void AddItem(Product product, int quantity) diff --git a/src/Samples/Sample.AspNetCore/Models/MerchantSettings.cs b/src/Samples/Sample.AspNetCore/Models/MerchantSettings.cs index 0ecc10b4..9a5d5607 100644 --- a/src/Samples/Sample.AspNetCore/Models/MerchantSettings.cs +++ b/src/Samples/Sample.AspNetCore/Models/MerchantSettings.cs @@ -9,5 +9,6 @@ public class MerchantSettings public Uri CheckoutUri { get; set; } public Uri ConfirmationUri { get; set; } public Uri CheckoutValidationCallbackUri { get; set; } + public Uri WebhookUri { get; set; } } } diff --git a/src/Samples/Sample.AspNetCore/Models/Order.cs b/src/Samples/Sample.AspNetCore/Models/Order.cs index 05b1f261..d16fcf40 100644 --- a/src/Samples/Sample.AspNetCore/Models/Order.cs +++ b/src/Samples/Sample.AspNetCore/Models/Order.cs @@ -2,16 +2,21 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace Sample.AspNetCore.Models +namespace Sample.AspNetCore.Models; + +public class Order { - public class Order - { - [BindNever] public ICollection Lines { get; set; } + [BindNever] public ICollection Lines { get; set; } + + [BindNever] + public int OrderId { get; set; } + + [BindNever] + public string ShippingStatus { get; set; } - [BindNever] - public int OrderId { get; set; } + [BindNever] + public string ShippingDescription { get; set; } - [Key] - public string SveaOrderId { get; set; } - } + [Key] + public string SveaOrderId { get; set; } } \ No newline at end of file diff --git a/src/Samples/Sample.AspNetCore/Models/ProductGenerator.cs b/src/Samples/Sample.AspNetCore/Models/ProductGenerator.cs index e99c4c46..24e4a157 100644 --- a/src/Samples/Sample.AspNetCore/Models/ProductGenerator.cs +++ b/src/Samples/Sample.AspNetCore/Models/ProductGenerator.cs @@ -45,7 +45,8 @@ public static void Initialize(IServiceProvider serviceProvider) Name = "Levis 512 Jeans", Reference = "Ref3", Price = 1000, - DiscountAmount = 100 + DiscountAmount = 100, + VatPercentage = 10 }, new Product { @@ -55,7 +56,8 @@ public static void Initialize(IServiceProvider serviceProvider) Name = "Levis 520 Jeans", Reference = "Ref4", Price = 1000, - DiscountPercent = 20 + DiscountPercent = 20, + VatPercentage = 20 }); context.SaveChanges(); diff --git a/src/Samples/Sample.AspNetCore/Models/ViewModels/OrderViewModel.cs b/src/Samples/Sample.AspNetCore/Models/ViewModels/OrderViewModel.cs index d1a30858..f9c7b14a 100644 --- a/src/Samples/Sample.AspNetCore/Models/ViewModels/OrderViewModel.cs +++ b/src/Samples/Sample.AspNetCore/Models/ViewModels/OrderViewModel.cs @@ -1,15 +1,17 @@ -namespace Sample.AspNetCore.Models.ViewModels -{ - using Svea.WebPay.SDK.PaymentAdminApi.Models; +namespace Sample.AspNetCore.Models.ViewModels; + +using Svea.WebPay.SDK.PaymentAdminApi.Models; - public class OrderViewModel +public class OrderViewModel +{ + public OrderViewModel(long orderId) { - public OrderViewModel(int orderId) - { - OrderId = orderId; - } - public int OrderId { get; set; } - public bool IsLoaded { get; set; } - public Order Order { get; set; } + OrderId = orderId; } + + public long OrderId { get; set; } + public bool IsLoaded { get; set; } + public Order Order { get; set; } + public string ShippingStatus { get; set; } + public string ShippingDescription { get; set; } } \ No newline at end of file diff --git a/src/Samples/Sample.AspNetCore/Startup.cs b/src/Samples/Sample.AspNetCore/Startup.cs index 141920a9..9e71135a 100644 --- a/src/Samples/Sample.AspNetCore/Startup.cs +++ b/src/Samples/Sample.AspNetCore/Startup.cs @@ -11,9 +11,12 @@ namespace Sample.AspNetCore { + using Svea.WebPay.SDK.Json; + using System; using System.Collections.Generic; using System.Linq; + using System.Text.Json; using JsonSerializer = System.Text.Json.JsonSerializer; @@ -46,7 +49,7 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient(s => merchantSettings); services.AddDbContext(options => options.UseInMemoryDatabase("Products")); services.AddControllersWithViews(); - services.AddControllers(); + services.AddControllers().AddJsonOptions(opt => opt.JsonSerializerOptions.PropertyNameCaseInsensitive = true); services.AddDistributedMemoryCache(); services.Configure(Configuration.GetSection("MerchantSettings")); @@ -55,18 +58,6 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient(); - Console.WriteLine("Credentials"); - Console.WriteLine(JsonSerializer.Serialize(credentialsSettings)); - Console.WriteLine(JsonSerializer.Serialize(credentials)); - - Console.WriteLine("MerchantSettings"); - Console.WriteLine(JsonSerializer.Serialize(merchantSettingsSettings)); - Console.WriteLine(JsonSerializer.Serialize(merchantSettings)); - - Console.WriteLine("SveaApiUrls"); - Console.WriteLine(JsonSerializer.Serialize(sveaApiUrlsSettings)); - Console.WriteLine(JsonSerializer.Serialize(sveaApiUrls)); - var credential = credentials.FirstOrDefault(); services.AddSveaClient(sveaApiUrls.CheckoutApiUri, sveaApiUrls.PaymentAdminApiUri, credential?.MerchantId, credential?.Secret); services.AddSession(); diff --git a/src/Samples/Sample.AspNetCore/Views/Orders/Details.cshtml b/src/Samples/Sample.AspNetCore/Views/Orders/Details.cshtml index f732463c..596ccd8c 100644 --- a/src/Samples/Sample.AspNetCore/Views/Orders/Details.cshtml +++ b/src/Samples/Sample.AspNetCore/Views/Orders/Details.cshtml @@ -49,13 +49,17 @@ else if (TempData["ErrorMessage"] != null)

Order #@count

+ + + +
@Html.DisplayNameFor(model => order.OrderId)
- @Html.DisplayFor(model => order.OrderId) + @Html.DisplayFor(model => order.Order.Id)
@if (order.IsLoaded) { @@ -78,6 +82,23 @@ else if (TempData["ErrorMessage"] != null)
@Html.DisplayFor(model => order.Order.PaymentType)
+ + @if (!string.IsNullOrWhiteSpace(order.ShippingStatus)) + { +
+ @Html.DisplayNameFor(model => order.ShippingStatus) +
+
+ @Html.DisplayFor(model => order.ShippingStatus) +
+ +
+ @Html.DisplayNameFor(model => order.ShippingDescription) +
+
+ @Html.DisplayFor(model => order.ShippingDescription) +
+ } } else { diff --git a/src/Samples/Sample.AspNetCore/Views/Products/Index.cshtml b/src/Samples/Sample.AspNetCore/Views/Products/Index.cshtml index af16c87e..05043399 100644 --- a/src/Samples/Sample.AspNetCore/Views/Products/Index.cshtml +++ b/src/Samples/Sample.AspNetCore/Views/Products/Index.cshtml @@ -41,10 +41,13 @@ else if (TempData["ErrorMessage"] != null) @Html.DisplayNameFor(model => model.Price) - Discount Amount + @Html.DisplayNameFor(model => model.DiscountAmount) - Discount Percent + @Html.DisplayNameFor(model => model.DiscountPercent) + + + @Html.DisplayNameFor(model => model.VatPercentage) @@ -60,6 +63,8 @@ else if (TempData["ErrorMessage"] != null) @((item.Price).ToString("c", CultureInfo.CreateSpecificCulture("sv-SE"))) @(item.DiscountAmount != 0 ? item.DiscountAmount.ToString("c", CultureInfo.CreateSpecificCulture("sv-SE")) : null) @(item.DiscountPercent != 0 ? item.DiscountPercent.ToString() + "%" : null) + @(item.VatPercentage != 0 ? item.VatPercentage.ToString() + "%" : null) + Add to Cart } diff --git a/src/Samples/Sample.AspNetCore/Views/Shared/Components/CartSummary/Default.cshtml b/src/Samples/Sample.AspNetCore/Views/Shared/Components/CartSummary/Default.cshtml index 330cfcd7..b2097fa6 100644 --- a/src/Samples/Sample.AspNetCore/Views/Shared/Components/CartSummary/Default.cshtml +++ b/src/Samples/Sample.AspNetCore/Views/Shared/Components/CartSummary/Default.cshtml @@ -76,18 +76,49 @@ \ No newline at end of file diff --git a/src/Samples/Sample.AspNetCore/appsettings.json b/src/Samples/Sample.AspNetCore/appsettings.json index 48526b6a..896723be 100644 --- a/src/Samples/Sample.AspNetCore/appsettings.json +++ b/src/Samples/Sample.AspNetCore/appsettings.json @@ -81,6 +81,7 @@ "TermsUri": "https://svea-sample.ngrok.io/terms", "CheckoutUri": "https://localhost:44345/CheckOut/LoadPaymentMenu", "ConfirmationUri": "https://localhost:44345/checkout/thankyou", - "CheckoutValidationCallbackUri": "https://svea-sample.ngrok.io/api/svea/validation/{checkout.order.uri}/?marketId={marketId}" + "CheckoutValidationCallbackUri": "https://svea-sample.ngrok.io/api/svea/validation/{checkout.order.uri}/?marketId={marketId}", + "WebhookUri": "https://svea-sample.ngrok.io/api/svea/shippingvalidation/?marketId={marketId}" } } diff --git a/src/Samples/Sample.AspNetCore/ngrok.yml b/src/Samples/Sample.AspNetCore/ngrok.yml index 2f7204c3..468168c6 100644 --- a/src/Samples/Sample.AspNetCore/ngrok.yml +++ b/src/Samples/Sample.AspNetCore/ngrok.yml @@ -12,4 +12,8 @@ tunnels: uitestpro: proto: http addr: https://localhost:5001 - subdomain: uitest-pro-svea-webpay-sdk \ No newline at end of file + subdomain: uitest-pro-svea-webpay-sdk + uitestGithub: + proto: http + addr: https://localhost:5001 + subdomain: \ No newline at end of file diff --git a/src/Samples/Sample.AspNetCore/wwwroot/js/site.js b/src/Samples/Sample.AspNetCore/wwwroot/js/site.js index a8f62078..a1e1a9ab 100644 --- a/src/Samples/Sample.AspNetCore/wwwroot/js/site.js +++ b/src/Samples/Sample.AspNetCore/wwwroot/js/site.js @@ -1,8 +1,4 @@ -// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification -// for details on configuring this project to bundle and minify static web assets. - -// Write your Javascript code. -var coll = document.getElementsByClassName("collapsible"); +var coll = document.getElementsByClassName("collapsible"); var i; for (i = 0; i < coll.length; i++) { @@ -18,7 +14,38 @@ for (i = 0; i < coll.length; i++) { } var updateSettings = function(element) { - var form = element.closest('form'); + let form = element.closest('form'); form.find('[type="hidden"]:first').val(element.html()); form.submit(); -}; \ No newline at end of file +}; + +var shippingHandler = function (data) { + console.log('event: ' + data); + + if (data.detail) { + document.dispatchEvent(new CustomEvent("sveaCheckout:setIsLoading", { detail: { isLoading: true } }));; + + fetch('/api/svea/shippingTaxCalculation', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data.detail), + }) + .then(response => { + console.log(response); + }) + .then(data => { + console.log('Success:', data); + }) + .catch((error) => { + console.error('Error:', error); + }); + + document.dispatchEvent(new CustomEvent("sveaCheckout:setIsLoading", { detail: { isLoading: false } })); + } +} + +$(function () { + document.addEventListener("sveaCheckout:shippingConfirmed", shippingHandler); +}); \ No newline at end of file diff --git a/src/Sample.AspNetFramework/App_Start/BundleConfig.cs b/src/Samples/Sample.AspNetFramework/App_Start/BundleConfig.cs similarity index 100% rename from src/Sample.AspNetFramework/App_Start/BundleConfig.cs rename to src/Samples/Sample.AspNetFramework/App_Start/BundleConfig.cs diff --git a/src/Sample.AspNetFramework/App_Start/FilterConfig.cs b/src/Samples/Sample.AspNetFramework/App_Start/FilterConfig.cs similarity index 100% rename from src/Sample.AspNetFramework/App_Start/FilterConfig.cs rename to src/Samples/Sample.AspNetFramework/App_Start/FilterConfig.cs diff --git a/src/Sample.AspNetFramework/App_Start/RouteConfig.cs b/src/Samples/Sample.AspNetFramework/App_Start/RouteConfig.cs similarity index 100% rename from src/Sample.AspNetFramework/App_Start/RouteConfig.cs rename to src/Samples/Sample.AspNetFramework/App_Start/RouteConfig.cs diff --git a/src/Sample.AspNetFramework/Content/Site.css b/src/Samples/Sample.AspNetFramework/Content/Site.css similarity index 100% rename from src/Sample.AspNetFramework/Content/Site.css rename to src/Samples/Sample.AspNetFramework/Content/Site.css diff --git a/src/Sample.AspNetFramework/Content/bootstrap-theme.css b/src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.css similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap-theme.css rename to src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.css diff --git a/src/Sample.AspNetFramework/Content/bootstrap-theme.css.map b/src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.css.map similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap-theme.css.map rename to src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.css.map diff --git a/src/Sample.AspNetFramework/Content/bootstrap-theme.min.css b/src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.min.css similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap-theme.min.css rename to src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.min.css diff --git a/src/Sample.AspNetFramework/Content/bootstrap-theme.min.css.map b/src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.min.css.map similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap-theme.min.css.map rename to src/Samples/Sample.AspNetFramework/Content/bootstrap-theme.min.css.map diff --git a/src/Sample.AspNetFramework/Content/bootstrap.css b/src/Samples/Sample.AspNetFramework/Content/bootstrap.css similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap.css rename to src/Samples/Sample.AspNetFramework/Content/bootstrap.css diff --git a/src/Sample.AspNetFramework/Content/bootstrap.css.map b/src/Samples/Sample.AspNetFramework/Content/bootstrap.css.map similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap.css.map rename to src/Samples/Sample.AspNetFramework/Content/bootstrap.css.map diff --git a/src/Sample.AspNetFramework/Content/bootstrap.min.css b/src/Samples/Sample.AspNetFramework/Content/bootstrap.min.css similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap.min.css rename to src/Samples/Sample.AspNetFramework/Content/bootstrap.min.css diff --git a/src/Sample.AspNetFramework/Content/bootstrap.min.css.map b/src/Samples/Sample.AspNetFramework/Content/bootstrap.min.css.map similarity index 100% rename from src/Sample.AspNetFramework/Content/bootstrap.min.css.map rename to src/Samples/Sample.AspNetFramework/Content/bootstrap.min.css.map diff --git a/src/Sample.AspNetFramework/Controllers/HomeController.cs b/src/Samples/Sample.AspNetFramework/Controllers/HomeController.cs similarity index 100% rename from src/Sample.AspNetFramework/Controllers/HomeController.cs rename to src/Samples/Sample.AspNetFramework/Controllers/HomeController.cs diff --git a/src/Sample.AspNetFramework/Global.asax b/src/Samples/Sample.AspNetFramework/Global.asax similarity index 100% rename from src/Sample.AspNetFramework/Global.asax rename to src/Samples/Sample.AspNetFramework/Global.asax diff --git a/src/Sample.AspNetFramework/Global.asax.cs b/src/Samples/Sample.AspNetFramework/Global.asax.cs similarity index 100% rename from src/Sample.AspNetFramework/Global.asax.cs rename to src/Samples/Sample.AspNetFramework/Global.asax.cs diff --git a/src/Sample.AspNetFramework/Models/HomeViewModel.cs b/src/Samples/Sample.AspNetFramework/Models/HomeViewModel.cs similarity index 100% rename from src/Sample.AspNetFramework/Models/HomeViewModel.cs rename to src/Samples/Sample.AspNetFramework/Models/HomeViewModel.cs diff --git a/src/Sample.AspNetFramework/Properties/AssemblyInfo.cs b/src/Samples/Sample.AspNetFramework/Properties/AssemblyInfo.cs similarity index 100% rename from src/Sample.AspNetFramework/Properties/AssemblyInfo.cs rename to src/Samples/Sample.AspNetFramework/Properties/AssemblyInfo.cs diff --git a/src/Sample.AspNetFramework/Sample.AspNetFramework.csproj b/src/Samples/Sample.AspNetFramework/Sample.AspNetFramework.csproj similarity index 100% rename from src/Sample.AspNetFramework/Sample.AspNetFramework.csproj rename to src/Samples/Sample.AspNetFramework/Sample.AspNetFramework.csproj diff --git a/src/Sample.AspNetFramework/Sample.AspNetFramework.csproj.user b/src/Samples/Sample.AspNetFramework/Sample.AspNetFramework.csproj.user similarity index 100% rename from src/Sample.AspNetFramework/Sample.AspNetFramework.csproj.user rename to src/Samples/Sample.AspNetFramework/Sample.AspNetFramework.csproj.user diff --git a/src/Sample.AspNetFramework/Scripts/bootstrap.js b/src/Samples/Sample.AspNetFramework/Scripts/bootstrap.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/bootstrap.js rename to src/Samples/Sample.AspNetFramework/Scripts/bootstrap.js diff --git a/src/Sample.AspNetFramework/Scripts/bootstrap.min.js b/src/Samples/Sample.AspNetFramework/Scripts/bootstrap.min.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/bootstrap.min.js rename to src/Samples/Sample.AspNetFramework/Scripts/bootstrap.min.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.intellisense.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.intellisense.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.intellisense.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.intellisense.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.map b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.map similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.map rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.min.map diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.map b/src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.map similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.map rename to src/Samples/Sample.AspNetFramework/Scripts/jquery-3.4.1.slim.min.map diff --git a/src/Sample.AspNetFramework/Scripts/jquery.validate-vsdoc.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery.validate-vsdoc.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery.validate-vsdoc.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery.validate-vsdoc.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery.validate.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery.validate.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery.validate.min.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.min.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery.validate.min.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.min.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.js diff --git a/src/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.min.js b/src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.min.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.min.js rename to src/Samples/Sample.AspNetFramework/Scripts/jquery.validate.unobtrusive.min.js diff --git a/src/Sample.AspNetFramework/Scripts/modernizr-2.8.3.js b/src/Samples/Sample.AspNetFramework/Scripts/modernizr-2.8.3.js similarity index 100% rename from src/Sample.AspNetFramework/Scripts/modernizr-2.8.3.js rename to src/Samples/Sample.AspNetFramework/Scripts/modernizr-2.8.3.js diff --git a/src/Sample.AspNetFramework/Views/Home/About.cshtml b/src/Samples/Sample.AspNetFramework/Views/Home/About.cshtml similarity index 100% rename from src/Sample.AspNetFramework/Views/Home/About.cshtml rename to src/Samples/Sample.AspNetFramework/Views/Home/About.cshtml diff --git a/src/Sample.AspNetFramework/Views/Home/Contact.cshtml b/src/Samples/Sample.AspNetFramework/Views/Home/Contact.cshtml similarity index 100% rename from src/Sample.AspNetFramework/Views/Home/Contact.cshtml rename to src/Samples/Sample.AspNetFramework/Views/Home/Contact.cshtml diff --git a/src/Sample.AspNetFramework/Views/Home/Index.cshtml b/src/Samples/Sample.AspNetFramework/Views/Home/Index.cshtml similarity index 100% rename from src/Sample.AspNetFramework/Views/Home/Index.cshtml rename to src/Samples/Sample.AspNetFramework/Views/Home/Index.cshtml diff --git a/src/Sample.AspNetFramework/Views/Shared/Error.cshtml b/src/Samples/Sample.AspNetFramework/Views/Shared/Error.cshtml similarity index 100% rename from src/Sample.AspNetFramework/Views/Shared/Error.cshtml rename to src/Samples/Sample.AspNetFramework/Views/Shared/Error.cshtml diff --git a/src/Sample.AspNetFramework/Views/Shared/_Layout.cshtml b/src/Samples/Sample.AspNetFramework/Views/Shared/_Layout.cshtml similarity index 100% rename from src/Sample.AspNetFramework/Views/Shared/_Layout.cshtml rename to src/Samples/Sample.AspNetFramework/Views/Shared/_Layout.cshtml diff --git a/src/Sample.AspNetFramework/Views/Web.config b/src/Samples/Sample.AspNetFramework/Views/Web.config similarity index 100% rename from src/Sample.AspNetFramework/Views/Web.config rename to src/Samples/Sample.AspNetFramework/Views/Web.config diff --git a/src/Sample.AspNetFramework/Views/_ViewStart.cshtml b/src/Samples/Sample.AspNetFramework/Views/_ViewStart.cshtml similarity index 100% rename from src/Sample.AspNetFramework/Views/_ViewStart.cshtml rename to src/Samples/Sample.AspNetFramework/Views/_ViewStart.cshtml diff --git a/src/Sample.AspNetFramework/Web.Debug.config b/src/Samples/Sample.AspNetFramework/Web.Debug.config similarity index 100% rename from src/Sample.AspNetFramework/Web.Debug.config rename to src/Samples/Sample.AspNetFramework/Web.Debug.config diff --git a/src/Sample.AspNetFramework/Web.Release.config b/src/Samples/Sample.AspNetFramework/Web.Release.config similarity index 100% rename from src/Sample.AspNetFramework/Web.Release.config rename to src/Samples/Sample.AspNetFramework/Web.Release.config diff --git a/src/Sample.AspNetFramework/Web.config b/src/Samples/Sample.AspNetFramework/Web.config similarity index 100% rename from src/Sample.AspNetFramework/Web.config rename to src/Samples/Sample.AspNetFramework/Web.config diff --git a/src/Sample.AspNetFramework/favicon.ico b/src/Samples/Sample.AspNetFramework/favicon.ico similarity index 100% rename from src/Sample.AspNetFramework/favicon.ico rename to src/Samples/Sample.AspNetFramework/favicon.ico diff --git a/src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.eot b/src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.eot rename to src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.eot diff --git a/src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.svg b/src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.svg rename to src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.svg diff --git a/src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.ttf b/src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.ttf rename to src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.ttf diff --git a/src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff b/src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff rename to src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff diff --git a/src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff2 b/src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from src/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff2 rename to src/Samples/Sample.AspNetFramework/fonts/glyphicons-halflings-regular.woff2 diff --git a/src/Sample.AspNetFramework/packages.config b/src/Samples/Sample.AspNetFramework/packages.config similarity index 100% rename from src/Sample.AspNetFramework/packages.config rename to src/Samples/Sample.AspNetFramework/packages.config diff --git a/src/Svea.WebPay.SDK.Tests/CheckoutTests.cs b/src/Svea.WebPay.SDK.Tests/CheckoutTests.cs index c6548f07..b7cbcb12 100644 --- a/src/Svea.WebPay.SDK.Tests/CheckoutTests.cs +++ b/src/Svea.WebPay.SDK.Tests/CheckoutTests.cs @@ -67,6 +67,7 @@ public async System.Threading.Tasks.Task UpdateOrder_Should_Serialize_AsExpected Assert.Equal(6, actualOrder.Cart.Items[0].VatPercent); } + [Fact] public void Order_Should_Serialize_AsExpected() { diff --git a/src/Svea.WebPay.SDK.Tests/Helpers/DataComparison.cs b/src/Svea.WebPay.SDK.Tests/Helpers/DataComparison.cs index 1a5dd7ed..eaccdf50 100644 --- a/src/Svea.WebPay.SDK.Tests/Helpers/DataComparison.cs +++ b/src/Svea.WebPay.SDK.Tests/Helpers/DataComparison.cs @@ -3,7 +3,7 @@ using System.Linq; using Xunit; using PaymentOrderRow = Svea.WebPay.SDK.PaymentAdminApi.Models.OrderRow; -using CheckoutOrderRow = Svea.WebPay.SDK.CheckoutApi.OrderRowResponse; +using CheckoutOrderRow = Svea.WebPay.SDK.CheckoutApi.OrderRow; namespace Svea.WebPay.SDK.Tests.Helpers { diff --git a/src/Svea.WebPay.SDK.Tests/Helpers/DataSample.cs b/src/Svea.WebPay.SDK.Tests/Helpers/DataSample.cs index ae303b9d..7e56f500 100644 --- a/src/Svea.WebPay.SDK.Tests/Helpers/DataSample.cs +++ b/src/Svea.WebPay.SDK.Tests/Helpers/DataSample.cs @@ -738,6 +738,59 @@ public static class DataSample ""PeppolId"":null }"; + public static string CheckoutOrderWithShippingResponse = @" + { + ""Gui"": + { + ""Layout"":""desktop"", + ""Snippet"":""\r\n
\r\n \r\n \r\n
\r\n\r\n\r\n\r\n\r\n"" + }, + ""MerchantSettings"": + { + ""CheckoutValidationCallBackUri"":""https://svea-sample.ngrok.io/api/svea/validation/{checkout.order.uri}/?marketId=SE"", + ""PushUri"":""https://svea-sample.ngrok.io/api/svea/push/{checkout.order.uri}/?marketId=SE"", + ""TermsUri"":""https://svea-sample.ngrok.io/terms"", + ""CheckoutUri"":""https://localhost:44345/CheckOut/LoadPaymentMenu"", + ""ConfirmationUri"":""https://localhost:44345/checkout/thankyou"", + ""ActivePartPaymentCampaigns"":[], + ""PromotedPartPaymentCampaign"":0} + ,""Cart"": + { + ""Items"":[ + {""ArticleNumber"":""Ref2"",""Name"":""Levis 501 Jeans"",""Quantity"":100,""UnitPrice"":119000,""DiscountPercent"":0,""DiscountAmount"":0,""VatPercent"":0,""Unit"":null,""TemporaryReference"":null,""RowNumber"":1,""MerchantData"":null,""ShippingInformation"":null,""RowType"":""Row""}]}, + ""Customer"":null, + ""ShippingAddress"":null, + ""BillingAddress"":null, + ""Locale"":""sv-SE"", + ""Currency"":""SEK"", + ""CountryCode"":""SE"", + ""ClientOrderNumber"":""637919465008154201"", + ""OrderId"":8753028, + ""EmailAddress"":null, + ""PhoneNumber"":null, + ""PaymentType"":null, + ""Payment"":null, + ""Status"":""Created"", + ""CustomerReference"":null, + ""SveaWillBuyOrder"":null, + ""IdentityFlags"":null, + ""MerchantData"":null, + ""PeppolId"":null, + ""ShippingInformation"": + { + ""ShippingProvider"": { + ""Name"": ""nShift"", + ""ShipmentId"": """", + ""ShippingOption"": null + }, + ""EnforceFallback"":false, + ""EnableShipping"":true, + ""Weight"":1000.0, + ""Tags"":null, + ""FallbackOptions"":null + } + }"; + public static string CheckoutGetOrderResponse = @" { ""MerchantSettings"": diff --git a/src/Svea.WebPay.SDK.Tests/Json/JsonConvertTests.cs b/src/Svea.WebPay.SDK.Tests/Json/JsonConvertTests.cs index 860183c8..e03a1e82 100644 --- a/src/Svea.WebPay.SDK.Tests/Json/JsonConvertTests.cs +++ b/src/Svea.WebPay.SDK.Tests/Json/JsonConvertTests.cs @@ -1,16 +1,26 @@ using System.Collections.Generic; -using Svea.WebPay.SDK.PaymentAdminApi; + using Xunit; namespace Svea.WebPay.SDK.Tests.Json { + using Svea.WebPay.SDK.CheckoutApi; using Svea.WebPay.SDK.Json; using Svea.WebPay.SDK.PaymentAdminApi.Response; + using Svea.WebPay.SDK.Tests.Helpers; using System.Text.Json; + using PaymentType = Svea.WebPay.SDK.PaymentAdminApi.PaymentType; + public class JsonConvertTests { + [Fact] + public void CanDeserialize_DataResponseWithShippingObject() + { + var result = DataSample.CheckoutOrderWithShippingResponse; + var data = JsonSerializer.Deserialize(result, JsonSerialization.Settings); + } [Fact] public void CanDeserialize_OrderResponseObject() diff --git a/src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj b/src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj index 1b76d616..d6927bfc 100644 --- a/src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj +++ b/src/Svea.WebPay.SDK.Tests/Svea.WebPay.SDK.Tests.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/Svea.WebPay.SDK.Tests/TestBase.cs b/src/Svea.WebPay.SDK.Tests/TestBase.cs index 5a4c990f..e8ec4271 100644 --- a/src/Svea.WebPay.SDK.Tests/TestBase.cs +++ b/src/Svea.WebPay.SDK.Tests/TestBase.cs @@ -10,6 +10,7 @@ namespace Svea.WebPay.SDK.Tests using Svea.WebPay.SDK.Tests.Helpers; using Svea.WebPay.SDK.Tests.Models; + using System.Linq; using System.Net; using System.Threading; @@ -27,7 +28,15 @@ public abstract class TestBase : IDisposable protected TestBase() { - var appRoot = AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf("bin", StringComparison.Ordinal)); + var appRoot = ""; + if (AppContext.BaseDirectory.IndexOf("bin", StringComparison.Ordinal) == -1) + { + appRoot = AppContext.BaseDirectory; + } + else + { + appRoot = AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf("bin", StringComparison.Ordinal)); + } Configuration = TestHelper.GetApplicationConfiguration(appRoot); diff --git a/src/Svea.WebPay.SDK.Tests/UnitTests/CartTests.cs b/src/Svea.WebPay.SDK.Tests/UnitTests/CartTests.cs new file mode 100644 index 00000000..95f038dc --- /dev/null +++ b/src/Svea.WebPay.SDK.Tests/UnitTests/CartTests.cs @@ -0,0 +1,50 @@ +namespace Svea.WebPay.SDK.Tests.UnitTests +{ + using Svea.WebPay.SDK.CheckoutApi; + + using System.Collections.Generic; + using System.Linq; + + using Xunit; + + public class CartTests + { + [Theory] + [MemberData(nameof(GetCalculateShippingOrderRowsTestData), parameters: 3)] + public void ShouldCalculateShippingOrderRows_AsExpected(IList orderRows, ShippingOption shippingOption, decimal expected) + { + var cart = new Cart(orderRows); + cart.CalculateShippingOrderRows(shippingOption); + long taxSum = 0; + var shippingRows = cart.Items.Where(x => x.RowType == RowType.ShippingFee.ToString()).ToList(); + foreach (var shippingOrderRow in shippingRows) + { + var tax = shippingOrderRow.UnitPrice.InLowestMonetaryUnit * (shippingOrderRow.VatPercent / 100) / 100; + taxSum += tax.InLowestMonetaryUnit; + } + + var actual = shippingRows.Sum(x => (x.UnitPrice.InLowestMonetaryUnit * (x.VatPercent / 100)) / 100); + Assert.Equal(expected, taxSum / 100M); + } + + public static IEnumerable GetCalculateShippingOrderRowsTestData(int numTests) + { + var orderRow1 = new OrderRow("articleNumber", "name", new MinorUnit(1), new MinorUnit(1000), new MinorUnit(200), new MinorUnit(25), "pcs", null, 1); + var orderRow2 = new OrderRow("articleNumber2", "name2", new MinorUnit(1), new MinorUnit(200), new MinorUnit(0), new MinorUnit(6), "pcs", null, 3); + var orderRow3 = new OrderRow("articleNumber2", "name2", new MinorUnit(2), new MinorUnit(350), new MinorUnit(0), new MinorUnit(12), "pcs", null, 2); + var orderRow4 = new OrderRow("articleNumber", "name", new MinorUnit(5), new MinorUnit(350), new MinorUnit(0), new MinorUnit(25), "pcs", null, 4); + + var shippingOption1 = new ShippingOption("875fb2cd-a570-4afb-8a66-177d3d613f81", 1, "DHL Home Delivery", "dhl", 100, "", "", "", 10000, 10000, null, null, null); + var shippingOption2 = new ShippingOption("875fb2cd-a570-4afb-8a66-177d3d613f81", 1, "DHL Home Delivery", "dhl", 50, "", "", "", 5000, 5000, null, null, null); + + var allData = new List + { + new object[] { new List { orderRow1, orderRow2, orderRow3, orderRow4 }, shippingOption1, 21.26M }, + new object[] { new List { orderRow1, orderRow2 }, shippingOption1, 80M * 0.25M + 20M * 0.06M }, + new object[] { new List { orderRow1, orderRow2 }, shippingOption2, 40M * 0.25M + 10M * 0.06M } + }; + + return allData.Take(numTests); + } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK.Tests/UnitTests/OrderRowResponseTests.cs b/src/Svea.WebPay.SDK.Tests/UnitTests/OrderRowResponseTests.cs index 36e6b1e4..5b20ead9 100644 --- a/src/Svea.WebPay.SDK.Tests/UnitTests/OrderRowResponseTests.cs +++ b/src/Svea.WebPay.SDK.Tests/UnitTests/OrderRowResponseTests.cs @@ -16,7 +16,7 @@ public void CreateOrderRowResponse_DoesNotThrow_WhenGivenValidNewOrderRowRespons int vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null) { //ACT - var ex = Record.Exception(() => new OrderRowResponse(articleNumber, name, + var ex = Record.Exception(() => new OrderRow(articleNumber, name, new MinorUnit(quantity), new MinorUnit(unitPrice), new MinorUnit(discountPercent), @@ -43,7 +43,7 @@ public void ThrowsArgumentException_WhenGivenInvalidOrderRowResponse(string arti int vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null) { //ASSERT - Assert.Throws(() => new OrderRowResponse(articleNumber, name, + Assert.Throws(() => new OrderRow(articleNumber, name, new MinorUnit(quantity), new MinorUnit(unitPrice), new MinorUnit(discountPercent), @@ -58,7 +58,7 @@ public void ThrowsArgumentException_IfMinorUnitIsNull(string articleNumber, stri int vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null) { //ASSERT - Assert.Throws(() => new OrderRowResponse(articleNumber, name, + Assert.Throws(() => new OrderRow(articleNumber, name, null, new MinorUnit(unitPrice), new MinorUnit(discountPercent), @@ -66,7 +66,7 @@ public void ThrowsArgumentException_IfMinorUnitIsNull(string articleNumber, stri new MinorUnit(vatPercent), unit, temporaryReference, rowNumber, merchantData)); - Assert.Throws(() => new OrderRowResponse(articleNumber, name, + Assert.Throws(() => new OrderRow(articleNumber, name, new MinorUnit(quantity), null, new MinorUnit(discountPercent), @@ -74,7 +74,7 @@ public void ThrowsArgumentException_IfMinorUnitIsNull(string articleNumber, stri new MinorUnit(vatPercent), unit, temporaryReference, rowNumber, merchantData)); - Assert.Throws(() => new OrderRowResponse(articleNumber, name, + Assert.Throws(() => new OrderRow(articleNumber, name, new MinorUnit(quantity), new MinorUnit(unitPrice), new MinorUnit(discountPercent), @@ -89,7 +89,7 @@ public void ThrowsArgumentException_IfNameIsNull(string articleNumber, int quant int vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null) { //ASSERT - Assert.Throws(() => new OrderRowResponse(articleNumber, null, + Assert.Throws(() => new OrderRow(articleNumber, null, new MinorUnit(quantity), new MinorUnit(unitPrice), new MinorUnit(discountPercent), diff --git a/src/Svea.WebPay.SDK.Tests/UnitTests/ShippingOptionTests.cs b/src/Svea.WebPay.SDK.Tests/UnitTests/ShippingOptionTests.cs new file mode 100644 index 00000000..30107f3a --- /dev/null +++ b/src/Svea.WebPay.SDK.Tests/UnitTests/ShippingOptionTests.cs @@ -0,0 +1,68 @@ +namespace Svea.WebPay.SDK.Tests.UnitTests; + +using Svea.WebPay.SDK.CheckoutApi; +using Svea.WebPay.SDK.Json; + +using System.Text.Json; + +using Xunit; + +public class ShippingOptionTests +{ + [Fact] + public void ShippingOptionJson_Should_DeserializeToObject() + { + var json = @"{ + ""id"":""7d9ec357-ba1a-4bcd-ace8-9ef69eac97d6"", + ""name"":""DHL Home Delivery"", + ""description"":""3-4 arbetsdagar"", + ""carrier"":""dhl"", + ""price"":200, + ""shippingFee"":20000, + ""totalShippingFee"":20000, + ""location"":{ + ""id"":""4210"", + ""name"":""Stockholm, Hägersten"", + ""address"":{ + ""city"":""Hägersten"", + ""countryCode"":""SE"", + ""postalCode"":""12630"", + ""streetAddress"":""Västberga Allé 41"", + ""streetAddress2"":null + } + }, + ""fields"":[ + { + ""id"":""FCRECEIVERDOORCODE"", + ""value"":"""" + }, + { + ""id"":""FCRECEIVERSMS"", + ""value"":""223•••••35"" + }, + { + ""id"":""FCRECEIVERPHONE"", + ""value"":""223•••••35"" + }, + { + ""id"":""FCDELIVERYINSTRUCTIONS"", + ""value"":""zsdfgs"" + } + ], + ""addons "":[], + ""timeslot "":"""", + ""postalCode "":""11338"", + ""orderId "":""8773762"" + }"; + + var shippingOption = JsonSerializer.Deserialize(json, JsonSerialization.Settings); + + Assert.Equal("7d9ec357-ba1a-4bcd-ace8-9ef69eac97d6", shippingOption?.Id); + Assert.Equal(200, shippingOption?.Price); + Assert.Equal(20000, shippingOption?.ShippingFee); + Assert.Equal("4210", shippingOption?.Location.Id); + Assert.Equal("Hägersten", shippingOption?.Location.Address.City); + Assert.Equal("FCRECEIVERSMS", shippingOption?.Fields[1].Id); + Assert.Equal("223•••••35", shippingOption?.Fields[2].Value); + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK.sln b/src/Svea.WebPay.SDK.sln index 56225cb7..ab393ed2 100644 --- a/src/Svea.WebPay.SDK.sln +++ b/src/Svea.WebPay.SDK.sln @@ -15,8 +15,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.AspNetCore.SystemTes EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{38DA8B44-EA0F-44E0-ADFD-DD61196EBB7D}" ProjectSection(SolutionItems) = preProject - ..\.github\workflows\build_test_publish.yml = ..\.github\workflows\build_test_publish.yml + ..\.github\workflows\deploy_pro_package_samplesite.yml = ..\.github\workflows\deploy_pro_package_samplesite.yml + ..\.github\workflows\dev.yml = ..\.github\workflows\dev.yml + ..\.github\workflows\pro.yml = ..\.github\workflows\pro.yml ..\README.md = ..\README.md + ..\.github\workflows\template_build.yml = ..\.github\workflows\template_build.yml + ..\.github\workflows\template_publish_webapp.yml = ..\.github\workflows\template_publish_webapp.yml + ..\.github\workflows\template_version.yml = ..\.github\workflows\template_version.yml + ..\.github\workflows\test_deploy_samplesite.yml = ..\.github\workflows\test_deploy_samplesite.yml + ..\.github\workflows\uat.yml = ..\.github\workflows\uat.yml EndProjectSection EndProject Global diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Addon.cs b/src/Svea.WebPay.SDK/CheckoutApi/Addon.cs new file mode 100644 index 00000000..f389813f --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/Addon.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Svea.WebPay.SDK.CheckoutApi +{ + public class Addon + { + public Addon(string id, List fields) + { + Id = id; + Fields = fields; + } + + public string Id { get; } + public List Fields { get; } + } +} diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Cart.cs b/src/Svea.WebPay.SDK/CheckoutApi/Cart.cs index d217b285..465036af 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/Cart.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/Cart.cs @@ -1,14 +1,35 @@ using System.Collections.Generic; +using System.Linq; namespace Svea.WebPay.SDK.CheckoutApi { - public class Cart + public class Cart { public Cart(IList items) { Items = items; } - public IList Items { get; } + public IList Items { get; internal set; } + + public void CalculateShippingOrderRows(ShippingOption shippingOption) + { + var totalSum = Items.Sum(x => x.Quantity * x.UnitPrice - x.DiscountAmount); + var taxGroup = Items.GroupBy(x => x.VatPercent); + + var rowNumber = Items.Max(x => x.RowNumber); + foreach (var tax in taxGroup) + { + rowNumber++; + var sumOnTaxValue = tax.Sum(x => x.Quantity * x.UnitPrice - x.DiscountAmount); + var percentageOnTaxValue = sumOnTaxValue / totalSum; + + var shippingPrice = percentageOnTaxValue * MinorUnit.FromLowestMonetaryUnit(shippingOption.ShippingFee); + + var price = new MinorUnit(shippingPrice); + var discount = new MinorUnit(0); + Items.Add(new OrderRow($"{shippingOption.Carrier}_{tax.Key}", $"{shippingOption.Carrier} VAT: {tax.Key}%", new MinorUnit(1), price, discount, tax.Key, "st", null, rowNumber, rowType: RowType.ShippingFee)); + } + } } } \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/CartResponse.cs b/src/Svea.WebPay.SDK/CheckoutApi/CartResponse.cs deleted file mode 100644 index 3795c178..00000000 --- a/src/Svea.WebPay.SDK/CheckoutApi/CartResponse.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Svea.WebPay.SDK.CheckoutApi -{ - public class CartResponse - { - public CartResponse(IList items) - { - Items = items; - } - - public IList Items { get; } - } -} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/CreateOrderModel.cs b/src/Svea.WebPay.SDK/CheckoutApi/CreateOrderModel.cs index 49b71043..9cc0caa8 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/CreateOrderModel.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/CreateOrderModel.cs @@ -13,42 +13,33 @@ public class CreateOrderModel /// /// /// - /// /// Locale for the order - /// /// Supported: sv-se, da-dk, de-de, en-us, fi-fi, nn-no. /// /// - /// /// A string that identifies the order in the merchant’s systems. The ClientOrderNumber is unique per order. /// Attempting to create a new order with a previously used ClientOrderNumber will result in the create method returning the already existing order instead. - /// /// Max length: 32. /// /// - /// /// At certain points in an order’s lifetime, we will attempt to call endpoints on your side. You provide the URI:s for these endpoints in the MerchantSettings object - /// /// /// /// /// /// /// - /// /// Provided by Svea to select partners. - /// /// Optional. /// /// - /// /// Metadata visible to the store - /// /// Max length: 6000. Optional. Cleaned up from Checkout database after 45 days. /// + /// Shipping information needed for the shipping checkout. Only applicable if merchant has shipping enabled. public CreateOrderModel(RegionInfo countryCode, CurrencyCode currency, Language locale, string clientOrderNumber, MerchantSettings merchantSettings, Cart cart, bool requireElectronicIdAuthentication, IList presetValues = null, - IdentityFlags identityFlags = null, Guid? partnerKey = null, string merchantData = null) + IdentityFlags identityFlags = null, Guid? partnerKey = null, string merchantData = null, ShippingInformation shippingInformation = null) { CountryCode = countryCode; Currency = currency; @@ -61,6 +52,40 @@ public CreateOrderModel(RegionInfo countryCode, CurrencyCode currency, Language IdentityFlags = identityFlags; PartnerKey = partnerKey; MerchantData = merchantData; + ShippingInformation = shippingInformation; + } + + /// + /// CreateOrderModel + /// + /// + /// + /// + /// Locale for the order + /// Supported: sv-se, da-dk, de-de, en-us, fi-fi, nn-no. + /// + /// + /// A string that identifies the order in the merchant’s systems. The ClientOrderNumber is unique per order. + /// Attempting to create a new order with a previously used ClientOrderNumber will result in the create method returning the already existing order instead. + /// Max length: 32. + /// + /// + /// At certain points in an order’s lifetime, we will attempt to call endpoints on your side. You provide the URI:s for these endpoints in the MerchantSettings object + /// + /// + /// + /// Shipping information needed for the shipping checkout. Only applicable if merchant has shipping enabled. + public CreateOrderModel(RegionInfo countryCode, CurrencyCode currency, Language locale, string clientOrderNumber, + MerchantSettings merchantSettings, Cart cart, bool requireElectronicIdAuthentication, ShippingInformation shippingInformation) + { + CountryCode = countryCode; + Currency = currency; + Locale = locale; + ClientOrderNumber = clientOrderNumber; + MerchantSettings = merchantSettings; + Cart = cart; + RequireElectronicIdAuthentication = requireElectronicIdAuthentication; + ShippingInformation = shippingInformation; } public RegionInfo CountryCode { get; } @@ -100,5 +125,10 @@ public CreateOrderModel(RegionInfo countryCode, CurrencyCode currency, Language /// /// Max length: 6000. Optional. Cleaned up from Checkout database after 45 days. public string MerchantData { get; } + + /// + /// Shipping information needed for the shipping checkout. Only applicable if merchant has shipping enabled. + /// + public ShippingInformation ShippingInformation { get; } } } \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Data.cs b/src/Svea.WebPay.SDK/CheckoutApi/Data.cs index 6d8bb92d..0c15cba7 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/Data.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/Data.cs @@ -1,7 +1,5 @@ namespace Svea.WebPay.SDK.CheckoutApi { - using Svea.WebPay.SDK.Json; - using System.Text.Json.Serialization; public class Data @@ -106,9 +104,9 @@ public Data() { } /// /// [JsonConstructor] - public Data(MerchantSettings merchantSettings, CartResponse cart, Customer customer, Address shippingAddress, Address billingAddress, Gui gui, string locale, string currency, + public Data(MerchantSettings merchantSettings, Cart cart, Customer customer, Address shippingAddress, Address billingAddress, Gui gui, string locale, string currency, string countryCode, Presetvalue[] presetValues, string clientOrderNumber, long orderId, string emailAddress, string phoneNumber, PaymentType? paymentType, - CheckoutOrderStatus status, object customerReference, bool? sveaWillBuyOrder, IdentityFlags identityFlags, object merchantData, PaymentInfo payment, string peppolId) + CheckoutOrderStatus status, object customerReference, bool? sveaWillBuyOrder, IdentityFlags identityFlags, object merchantData, PaymentInfo payment, string peppolId, GetOrderShippingInformation shippingInformation) { MerchantSettings = merchantSettings; Cart = cart; @@ -132,6 +130,7 @@ public Data(MerchantSettings merchantSettings, CartResponse cart, Customer custo MerchantData = merchantData; Payment = payment; PeppolId = peppolId; + ShippingInformation = shippingInformation; } /// @@ -145,7 +144,7 @@ public Data(MerchantSettings merchantSettings, CartResponse cart, Customer custo /// Order rows. /// [JsonInclude] - public CartResponse Cart { get; } + public Cart Cart { get; } /// /// Identified customer of the order. @@ -254,5 +253,8 @@ public Data(MerchantSettings merchantSettings, CartResponse cart, Customer custo /// [JsonInclude] public string PeppolId { get; } + + [JsonInclude] + public GetOrderShippingInformation ShippingInformation { get; } } } \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/FallbackOption.cs b/src/Svea.WebPay.SDK/CheckoutApi/FallbackOption.cs new file mode 100644 index 00000000..af9bc2f6 --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/FallbackOption.cs @@ -0,0 +1,59 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + using System; + using System.Collections.Generic; + using System.Text.Json.Serialization; + + public class FallbackOption : IShippingOption + { + /// + /// + /// + /// Id of the carrier, nShift specific (typically in form of a guid) + /// Name of the carrier, nShift specific + /// Delivery option name, nShift specific + /// Price of the parcel. Minor currency! + /// nshift format Addons. As smiliar as nshift models + /// nshift format Fields. As smiliar as nshift models + [JsonConstructor] + public FallbackOption(string id, string carrier, string name, MinorUnit shippingFee, List addons, List fields) + { + Id = id; + Carrier = carrier; + Name = name; + ShippingFee = shippingFee; + Addons = addons; + Fields = fields; + } + + /// + /// Id of the carrier, nShift specific (typically in form of a guid) + /// + public string Id { get; } + + /// + /// Name of the carrier, nShift specific + /// + public string Carrier { get; } + + /// + /// Delivery option name, nShift specific + /// + public string Name { get; } + + /// + /// Price of the parcel. Minor currency! + /// + public MinorUnit ShippingFee { get; } + + /// + /// nshift format Addons. As smiliar as nshift models + /// + public List Addons { get; } + + /// + /// nshift format Fields. As smiliar as nshift models + /// + public List Fields { get; } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Field.cs b/src/Svea.WebPay.SDK/CheckoutApi/Field.cs new file mode 100644 index 00000000..d526674b --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/Field.cs @@ -0,0 +1,14 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + public class Field + { + public Field(string id, string value) + { + Id = id; + Value = value; + } + + public string Id { get; } + public string Value { get; } + } +} diff --git a/src/Svea.WebPay.SDK/CheckoutApi/GetOrderShippingInformation.cs b/src/Svea.WebPay.SDK/CheckoutApi/GetOrderShippingInformation.cs new file mode 100644 index 00000000..2bcbefb9 --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/GetOrderShippingInformation.cs @@ -0,0 +1,24 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + using System.Collections.Generic; + + public class GetOrderShippingInformation : ShippingInformation + { + /// + /// + /// + /// In order to test how your fallback options will be demonstrated (Only for testing purposes) + /// If Shipping is enabled at Svea for the specific merchant and the property enableShipping = false then shipping will be disabled for that order (for example if the order is digital then the shipping can be disabled using this parameter, however merchant can also configure digital items in nShift for example if weight is set to 0) + /// Weight of the parcel in grams + /// + /// + /// + public GetOrderShippingInformation(bool enforceFallback, bool enableShipping, double weight, Dictionary tags, List fallbackOptions, ShippingProvider shippingProvider) + : base(enableShipping, weight, tags, fallbackOptions, enforceFallback) + { + ShippingProvider = shippingProvider; + } + + public ShippingProvider ShippingProvider { get; } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/IShippingOption.cs b/src/Svea.WebPay.SDK/CheckoutApi/IShippingOption.cs new file mode 100644 index 00000000..65d66c8f --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/IShippingOption.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Svea.WebPay.SDK.CheckoutApi +{ + public interface IShippingOption + { + string Id { get; } + string Carrier { get; } + string Name { get; } + List Addons { get; } + List Fields { get; } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Location.cs b/src/Svea.WebPay.SDK/CheckoutApi/Location.cs new file mode 100644 index 00000000..94809d6c --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/Location.cs @@ -0,0 +1,16 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + public class Location + { + public Location(string id, string name, Address address) + { + Id = id; + Name = name; + Address = address; + } + + public string Id { get; } + public string Name { get; } + public Address Address { get; } + } +} diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Merchantsettings.cs b/src/Svea.WebPay.SDK/CheckoutApi/Merchantsettings.cs index dc625882..d06b7409 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/Merchantsettings.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/Merchantsettings.cs @@ -49,6 +49,12 @@ public class MerchantSettings /// /// Data type: Url. Max length: 500. /// + /// + /// + /// URI of the webhook in order to be called by shipping services + /// + /// Data type: Url. Max length: 500 + /// /// /// /// List of valid CampaignIDs. If used, a list of available part payment campaign options will be filtered through the chosen list. @@ -62,7 +68,7 @@ public class MerchantSettings /// Must be valid CampaignID. /// public MerchantSettings(Uri pushUri, Uri termsUri, Uri checkoutUri, Uri confirmationUri, - Uri checkoutValidationCallBackUri = null, IList activePartPaymentCampaigns = null, + Uri checkoutValidationCallBackUri = null, Uri webhookUri = null, IList activePartPaymentCampaigns = null, long? promotedPartPaymentCampaign = null) { PushUri = pushUri ?? throw new ArgumentNullException(nameof(pushUri)); @@ -70,6 +76,7 @@ public MerchantSettings(Uri pushUri, Uri termsUri, Uri checkoutUri, Uri confirma CheckoutUri = checkoutUri ?? throw new ArgumentNullException(nameof(checkoutUri)); ConfirmationUri = confirmationUri ?? throw new ArgumentNullException(nameof(confirmationUri)); CheckoutValidationCallBackUri = checkoutValidationCallBackUri; + WebhookUri = webhookUri; ActivePartPaymentCampaigns = activePartPaymentCampaigns; PromotedPartPaymentCampaign = promotedPartPaymentCampaign; } @@ -126,5 +133,11 @@ public MerchantSettings(Uri pushUri, Uri termsUri, Uri checkoutUri, Uri confirma /// /// Must be valid CampaignID. public long? PromotedPartPaymentCampaign { get; } + + /// + /// URI of the webhook in order to be called by shipping services + /// + /// Data type: Url. Max length: 500 + public Uri WebhookUri { get; } } } \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/OrderRow.cs b/src/Svea.WebPay.SDK/CheckoutApi/OrderRow.cs index 904cf80e..0c43e7fc 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/OrderRow.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/OrderRow.cs @@ -1,72 +1,54 @@ namespace Svea.WebPay.SDK.CheckoutApi { using System; + using System.Text.Json.Serialization; public class OrderRow : OrderRowBase { /// /// /// - /// - /// Articlenumber as a string, can contain letters and numbers. - /// + /// Article number as a string, can contain letters and numbers. /// Max length: 256. Min length: 0. /// /// - /// /// Article name. - /// /// Max length: 40. Min length: 0. /// /// - /// /// Quantity of the product. 1-9 digits. - /// /// Required /// /// - /// /// Price of the product including VAT. 1-13 digits, can be negative. - /// /// Required /// /// - /// /// The discount of the product. Can be amount or percent based on the use of useDiscountPercent. - /// /// /// - /// /// The VAT percentage of the current product. Valid vat percentage for that country. - /// /// SE 6, 12, 25 /// /// - /// /// The unit type, e.g., “st”, “pc”, “kg” etc. - /// /// Max length: 4. Min length: 0. /// /// - /// /// Can be used when creating or updating an order. The returned rows will have their corresponding temporaryReference as they were given in the indata. /// It will not be stored and will not be returned in GetOrder. - /// /// /// - /// /// The row number the row will have in the Webpay system - /// /// /// - /// /// Metadata visible to the store - /// /// Max length: 255. Optional. Cleaned up from Checkout database after 45 days. /// /// Set to true if using percent in discount + /// Is used just to distinguish ShippingFee item from the order items. Can be one of "Row" or "ShippingFee" public OrderRow(string articleNumber, string name, MinorUnit quantity, MinorUnit unitPrice, MinorUnit discount, - MinorUnit vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null, bool useDiscountPercent = false) + MinorUnit vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null, bool useDiscountPercent = false, RowType rowType = CheckoutApi.RowType.Row) { ArticleNumber = articleNumber; Unit = unit; @@ -128,18 +110,126 @@ public OrderRow(string articleNumber, string name, MinorUnit quantity, MinorUnit { throw new ArgumentOutOfRangeException(nameof(merchantData), "Can only be 0-255 characters."); } + + RowType = rowType.ToString(); } /// - /// Can be used when creating or updating an order. The returned rows will have their corresponding temporaryreference as they were given in the indata. - /// It will not be stored and will not be returned in GetOrder. /// - public string TemporaryReference { get; } + /// + /// Article number as a string, can contain letters and numbers. + /// Max length: 256. Min length: 0. + /// + /// + /// Article name. + /// Max length: 40. Min length: 0. + /// + /// + /// Quantity of the product. 1-9 digits. + /// Required + /// + /// + /// Price of the product including VAT. 1-13 digits, can be negative. + /// Required + /// + /// + /// The discount percent of the product. Use this OR discountAmount + /// + /// + /// The discount amount of the product. Use this OR discountPercent + /// + /// + /// The VAT percentage of the current product. Valid vat percentage for that country. + /// SE 6, 12, 25 + /// + /// + /// The unit type, e.g., “st”, “pc”, “kg” etc. + /// Max length: 4. Min length: 0. + /// + /// + /// Can be used when creating or updating an order. The returned rows will have their corresponding temporaryReference as they were given in the indata. + /// It will not be stored and will not be returned in GetOrder. + /// + /// + /// The row number the row will have in the Webpay system + /// + /// + /// Metadata visible to the store + /// Max length: 255. Optional. Cleaned up from Checkout database after 45 days. + /// + [JsonConstructor] + public OrderRow(string articleNumber, string name, MinorUnit quantity, MinorUnit unitPrice, MinorUnit discountPercent, MinorUnit discountAmount, + MinorUnit vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null, string rowType = "Row") + { + ArticleNumber = articleNumber; + Unit = unit; + TemporaryReference = temporaryReference; + RowNumber = rowNumber; + MerchantData = merchantData; + DiscountPercent = discountPercent; + DiscountAmount = discountAmount; + + Name = name ?? throw new ArgumentNullException(nameof(name)); + Quantity = quantity ?? throw new ArgumentNullException(nameof(quantity)); + UnitPrice = unitPrice ?? throw new ArgumentNullException(nameof(unitPrice)); + VatPercent = vatPercent ?? throw new ArgumentNullException(nameof(vatPercent)); + + if (ArticleNumber?.Length > 256) + { + throw new ArgumentOutOfRangeException(nameof(articleNumber), "Maximum 256 characters."); + } + + if (Quantity.InLowestMonetaryUnit.ToString().Length > 9) + { + throw new ArgumentOutOfRangeException(nameof(quantity), "Value cannot be longer than 7 digits."); + } + + if (UnitPrice.InLowestMonetaryUnit.ToString().Length > 13) + { + throw new ArgumentOutOfRangeException(nameof(unitPrice), "Value cannot be longer than 11 digits."); + } + + if (DiscountAmount != null && DiscountAmount != 0) + { + if (DiscountAmount < 0) + { + throw new ArgumentOutOfRangeException(nameof(discountAmount), "Value cannot be less than zero."); + } + + if (DiscountAmount > unitPrice * quantity) + { + throw new ArgumentOutOfRangeException(nameof(discountAmount), "Value cannot be greater than unit price * quantity."); + } + } + + if (DiscountPercent != null && DiscountPercent.InLowestMonetaryUnit > 10000) + { + throw new ArgumentOutOfRangeException(nameof(discountPercent), "Value cannot be more than 100%."); + } + + if (Name.Length < 1 || Name.Length > 40) + { + throw new ArgumentOutOfRangeException(nameof(name), "Can only be 1-40 characters."); + } + + if (Unit?.Length > 4) + { + throw new ArgumentOutOfRangeException(nameof(unit), "Can only be 0-4 characters."); + } + + if (MerchantData?.Length > 255) + { + throw new ArgumentOutOfRangeException(nameof(merchantData), "Can only be 0-255 characters."); + } + + RowType = rowType; + } /// - /// The row number the row will have in the Webpay system + /// Can be used when creating or updating an order. The returned rows will have their corresponding temporaryreference as they were given in the indata. + /// It will not be stored and will not be returned in GetOrder. /// - public int RowNumber { get; } + public string TemporaryReference { get; } /// /// Metadata visible to the store diff --git a/src/Svea.WebPay.SDK/CheckoutApi/OrderRowResponse.cs b/src/Svea.WebPay.SDK/CheckoutApi/OrderRowResponse.cs deleted file mode 100644 index bda41617..00000000 --- a/src/Svea.WebPay.SDK/CheckoutApi/OrderRowResponse.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace Svea.WebPay.SDK.CheckoutApi -{ - using System; - using System.Text.Json.Serialization; - - public class OrderRowResponse : OrderRowBase - { - [JsonConstructor] - public OrderRowResponse(string articleNumber, string name, MinorUnit quantity, MinorUnit unitPrice, MinorUnit discountPercent, MinorUnit discountAmount, - MinorUnit vatPercent, string unit, string temporaryReference, int rowNumber, string merchantData = null) - { - ArticleNumber = articleNumber; - Unit = unit; - TemporaryReference = temporaryReference; - RowNumber = rowNumber; - MerchantData = merchantData; - DiscountPercent = discountPercent; - DiscountAmount = discountAmount; - - Name = name ?? throw new ArgumentNullException(nameof(name)); - Quantity = quantity ?? throw new ArgumentNullException(nameof(quantity)); - UnitPrice = unitPrice ?? throw new ArgumentNullException(nameof(unitPrice)); - VatPercent = vatPercent ?? throw new ArgumentNullException(nameof(vatPercent)); - - if (ArticleNumber?.Length > 256) - { - throw new ArgumentOutOfRangeException(nameof(articleNumber), "Maximum 256 characters."); - } - - if (Quantity.InLowestMonetaryUnit.ToString().Length > 9) - { - throw new ArgumentOutOfRangeException(nameof(quantity), "Value cannot be longer than 7 digits."); - } - - if (UnitPrice.InLowestMonetaryUnit.ToString().Length > 13) - { - throw new ArgumentOutOfRangeException(nameof(unitPrice), "Value cannot be longer than 11 digits."); - } - - if (DiscountAmount != null && DiscountAmount != 0) - { - if (DiscountAmount < 0) - { - throw new ArgumentOutOfRangeException(nameof(discountAmount), "Value cannot be less than zero."); - } - - if (DiscountAmount > unitPrice * quantity) - { - throw new ArgumentOutOfRangeException(nameof(discountAmount), "Value cannot be greater than unit price * quantity."); - } - } - - if (DiscountPercent != null && DiscountPercent.InLowestMonetaryUnit > 10000) - { - throw new ArgumentOutOfRangeException(nameof(discountPercent), "Value cannot be more than 100%."); - } - - if (Name.Length < 1 || Name.Length > 40) - { - throw new ArgumentOutOfRangeException(nameof(name), "Can only be 1-40 characters."); - } - - if (Unit?.Length > 4) - { - throw new ArgumentOutOfRangeException(nameof(unit), "Can only be 0-4 characters."); - } - - if (MerchantData?.Length > 255) - { - throw new ArgumentOutOfRangeException(nameof(merchantData), "Can only be 0-255 characters."); - } - } - - /// - /// Can be used when creating or updating an order. The returned rows will have their corresponding temporaryreference as they were given in the indata. - /// It will not be stored and will not be returned in GetOrder. - /// - public string TemporaryReference { get; } - - /// - /// The row number the row will have in the Webpay system - /// - public int RowNumber { get; } - - /// - /// Metadata visible to the store - /// - /// Max length: 255. Optional. Cleaned up from Checkout database after 45 days. - public string MerchantData { get; } - } -} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/CheckoutValidationCallbackResponse.cs b/src/Svea.WebPay.SDK/CheckoutApi/Response/CheckoutValidationCallbackResponse.cs similarity index 89% rename from src/Svea.WebPay.SDK/CheckoutApi/CheckoutValidationCallbackResponse.cs rename to src/Svea.WebPay.SDK/CheckoutApi/Response/CheckoutValidationCallbackResponse.cs index d7619f22..cb54fe0a 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/CheckoutValidationCallbackResponse.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/Response/CheckoutValidationCallbackResponse.cs @@ -1,4 +1,4 @@ -namespace Svea.WebPay.SDK.CheckoutApi +namespace Svea.WebPay.SDK.CheckoutApi.Response { public class CheckoutValidationCallbackResponse { diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingCallbackResponse.cs b/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingCallbackResponse.cs new file mode 100644 index 00000000..8f3abb0b --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingCallbackResponse.cs @@ -0,0 +1,20 @@ +namespace Svea.WebPay.SDK.CheckoutApi.Response +{ + using System.Text.Json.Serialization; + + public class ShippingCallbackResponse + { + public ShippingCallbackResponse(string type, string description, long orderId) + { + Type = type; + Description = description; + OrderId = orderId; + } + + public string Type { get; } + + public string Description { get; } + + public long OrderId { get; } + } +} diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingDescriptionResponse.cs b/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingDescriptionResponse.cs new file mode 100644 index 00000000..251efcd6 --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingDescriptionResponse.cs @@ -0,0 +1,15 @@ +namespace Svea.WebPay.SDK.CheckoutApi.Response +{ + internal class ShippingDescriptionResponse + { + internal ShippingDescriptionResponse(string tmsReference, ShippingOptionResponse selectedShippingOption) + { + TmsReference = tmsReference; + SelectedShippingOption = selectedShippingOption; + } + + internal string TmsReference { get; } + + internal ShippingOptionResponse SelectedShippingOption { get; } + } +} diff --git a/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingOptionResponse.cs b/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingOptionResponse.cs new file mode 100644 index 00000000..0b4853da --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/Response/ShippingOptionResponse.cs @@ -0,0 +1,14 @@ +namespace Svea.WebPay.SDK.CheckoutApi.Response +{ + internal class ShippingOptionResponse + { + internal ShippingOptionResponse(string id, string carrier) + { + Id = id; + Carrier = carrier; + } + + internal string Id { get; } + internal string Carrier { get; } + } +} diff --git a/src/Svea.WebPay.SDK/CheckoutApi/RowType.cs b/src/Svea.WebPay.SDK/CheckoutApi/RowType.cs new file mode 100644 index 00000000..4a3e5bb0 --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/RowType.cs @@ -0,0 +1,8 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + public enum RowType + { + Row, + ShippingFee + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/ShippingInformation.cs b/src/Svea.WebPay.SDK/CheckoutApi/ShippingInformation.cs new file mode 100644 index 00000000..f81976ca --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/ShippingInformation.cs @@ -0,0 +1,46 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + using System.Collections.Generic; + + public class ShippingInformation + { + /// + /// + /// + /// If Shipping is enabled at Svea for the specific merchant and the property enableShipping = false then shipping will be disabled for that order (for example if the order is digital then the shipping can be disabled using this parameter, however merchant can also configure digital items in nShift for example if weight is set to 0) + /// Weight of the parcel in grams + /// + /// A list of fallback options + /// In order to test how your fallback options will be demonstrated (Only for testing purposes) + public ShippingInformation(bool enableShipping, double weight, Dictionary tags, List fallbackOptions, bool enforceFallback = false) + { + EnableShipping = enableShipping; + Weight = weight; + Tags = tags; + FallbackOptions = fallbackOptions; + EnforceFallback = enforceFallback; + } + + /// + /// In order to test how your fallback options will be demonstrated (Only for testing purposes) + /// + public bool EnforceFallback { get; } + + /// + /// If Shipping is enabled at Svea for the specific merchant and the property enableShipping = false then shipping will be disabled for that order (for example if the order is digital then the shipping can be disabled using this parameter, however merchant can also configure digital items in nShift for example if weight is set to 0) + /// + public bool EnableShipping { get; } + + /// + /// Weight of the parcel in grams + /// + public double Weight { get; } + + public Dictionary Tags { get; } + + /// + /// A list of fallback options + /// + public List FallbackOptions { get; } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/ShippingOption.cs b/src/Svea.WebPay.SDK/CheckoutApi/ShippingOption.cs new file mode 100644 index 00000000..075e1351 --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/ShippingOption.cs @@ -0,0 +1,104 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + using System; + using System.Collections.Generic; + using System.Text.Json.Serialization; + + public class ShippingOption : IShippingOption + { + /// + /// + /// + /// Id of the carrier, nShift specific (typically in form of a guid) + /// Id of the order + /// Name of the carrier, nShift specific + /// Delivery option name, nShift specific + /// OBSOLETE (Please use 'shippingFee' property instead!) Price of the parcel, NOT minor currency! + /// + /// + /// + /// Price of the parcel. Minor currency! + /// + /// + /// nshift format Fields. As smiliar as nshift models. + /// nshift format Addons. As smiliar as nshift models. + [JsonConstructor] + public ShippingOption( + string id, + long orderId, + string carrier, + string name, + long price, + string description, + string postalCode, + string timeslot, + long shippingFee, + long totalShippingFee, + Location location, + List fields, + List addons) + { + Id = id; + OrderId = orderId; + Carrier = carrier; + Name = name; + Price = price; + Description = description; + PostalCode = postalCode; + Timeslot = timeslot; + ShippingFee = shippingFee; + TotalShippingFee = totalShippingFee; + Location = location; + Fields = fields; + Addons = addons; + } + + /// + /// Id of the carrier, nShift specific (typically in form of a guid) + /// + public string Id { get; } + + /// + /// Id of the order + /// + public long OrderId { get; } + + /// + /// Name of the carrier, nShift specific + /// + public string Carrier { get; } + + /// + /// Delivery option name, nShift specific + /// + public string Name { get; } + + /// + /// Price of the parcel, NOT minor currency! + /// + [Obsolete("OBSOLETE (Please use 'shippingFee' property instead!)")] + public long Price { get; } + + /// + /// Price of the parcel. Minor currency! + /// + public long ShippingFee { get; } + + public long TotalShippingFee { get; } + public Location Location { get; } + + /// + /// nshift format Fields. As smiliar as nshift models. + /// + public List Fields { get; } + + /// + /// nshift format Addons. As smiliar as nshift models + /// + public List Addons { get; } + + public string Description { get; } + public string PostalCode { get; } + public string Timeslot { get; } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/ShippingProvider.cs b/src/Svea.WebPay.SDK/CheckoutApi/ShippingProvider.cs new file mode 100644 index 00000000..cf652723 --- /dev/null +++ b/src/Svea.WebPay.SDK/CheckoutApi/ShippingProvider.cs @@ -0,0 +1,27 @@ +namespace Svea.WebPay.SDK.CheckoutApi +{ + public class ShippingProvider + { + public ShippingProvider(string name, string shipmentId, ShippingOption shippingOption) + { + Name = name; + ShipmentId = shipmentId; + ShippingOption = shippingOption; + } + + /// + /// Name of the shipping provider ('nShift' in this case). If shipping provider (nShift) is down and a fallback option is used then the value will be 'fallback' + /// + public string Name { get; } + + /// + /// Internal order Id, Id is only set if the order is final, otherwise empty string will be set. + /// + public string ShipmentId { get; } + + /// + /// Shipping option set on the order + /// + public ShippingOption ShippingOption { get; } + } +} \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/CheckoutApi/UpdateOrderModel.cs b/src/Svea.WebPay.SDK/CheckoutApi/UpdateOrderModel.cs index f71d5367..530ef25b 100644 --- a/src/Svea.WebPay.SDK/CheckoutApi/UpdateOrderModel.cs +++ b/src/Svea.WebPay.SDK/CheckoutApi/UpdateOrderModel.cs @@ -12,10 +12,12 @@ public class UpdateOrderModel /// /// Max length: 6000. Optional. Cleaned up from Checkout database after 45 days. /// - public UpdateOrderModel(Cart cart, string merchantData = null) + /// + public UpdateOrderModel(Cart cart, string merchantData = null, ShippingInformation shippingInformation = null) { Cart = cart; MerchantData = merchantData; + ShippingInformation = shippingInformation; } public Cart Cart { get; } @@ -24,6 +26,11 @@ public UpdateOrderModel(Cart cart, string merchantData = null) /// Metadata visible to the store /// /// Max length: 6000. Optional. Cleaned up from Checkout database after 45 days. - private string MerchantData { get; } + public string MerchantData { get; } + + /// + /// Shipping information to be updated. Only applicable if merchant has shipping enabled. + /// + public ShippingInformation ShippingInformation { get; } } } \ No newline at end of file diff --git a/src/Svea.WebPay.SDK/Json/CustomMinorUnitConverter.cs b/src/Svea.WebPay.SDK/Json/CustomMinorUnitConverter.cs index f8bf1917..df682ad0 100644 --- a/src/Svea.WebPay.SDK/Json/CustomMinorUnitConverter.cs +++ b/src/Svea.WebPay.SDK/Json/CustomMinorUnitConverter.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; namespace Svea.WebPay.SDK.Json { diff --git a/src/Svea.WebPay.SDK/OrderRowBase.cs b/src/Svea.WebPay.SDK/OrderRowBase.cs index 5e215002..9863f674 100644 --- a/src/Svea.WebPay.SDK/OrderRowBase.cs +++ b/src/Svea.WebPay.SDK/OrderRowBase.cs @@ -48,5 +48,16 @@ public class OrderRowBase /// /// Max length: 4. Min length: 0. public string Unit { get; internal set; } + + /// + /// The row number the row will have in the Webpay system + /// + public int RowNumber { get; internal set; } + + /// + /// Is used just to distinguish ShippingFee item from the order items. It is a string and can be one of "Row" or "ShippingFee" + /// + public string RowType { get; internal set; } + } } diff --git a/src/Svea.WebPay.SDK/PaymentAdminApi/Models/OrderRow.cs b/src/Svea.WebPay.SDK/PaymentAdminApi/Models/OrderRow.cs index 9105d8f3..5b4eb284 100644 --- a/src/Svea.WebPay.SDK/PaymentAdminApi/Models/OrderRow.cs +++ b/src/Svea.WebPay.SDK/PaymentAdminApi/Models/OrderRow.cs @@ -20,6 +20,7 @@ public OrderRow(long orderId, OrderRowResponseObject orderRowResponse, SveaHttpC Unit = orderRowResponse.Unit; IsCancelled = orderRowResponse.IsCancelled; AvailableActions = orderRowResponse.Actions; + RowType = orderRowResponse.RowType; } public IList AvailableActions { get; } diff --git a/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponse.cs b/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponse.cs index 19ebbf86..a0a4f803 100644 --- a/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponse.cs +++ b/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponse.cs @@ -2,7 +2,7 @@ { using System.Text.Json.Serialization; - public class AddOrderRowsResponse + internal class AddOrderRowsResponse { public AddOrderRowsResponse() { } diff --git a/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponseObject.cs b/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponseObject.cs index e9d5c359..1fb23021 100644 --- a/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponseObject.cs +++ b/src/Svea.WebPay.SDK/PaymentAdminApi/Response/AddOrderRowsResponseObject.cs @@ -2,7 +2,7 @@ { using System.Text.Json.Serialization; - public class AddOrderRowsResponseObject + internal class AddOrderRowsResponseObject { public AddOrderRowsResponseObject() { } diff --git a/src/Svea.WebPay.SDK/PaymentAdminApi/Response/CreditResponse.cs b/src/Svea.WebPay.SDK/PaymentAdminApi/Response/CreditResponse.cs index 26a3d635..ba501ba2 100644 --- a/src/Svea.WebPay.SDK/PaymentAdminApi/Response/CreditResponse.cs +++ b/src/Svea.WebPay.SDK/PaymentAdminApi/Response/CreditResponse.cs @@ -7,7 +7,7 @@ public class CreditResponse public CreditResponse() { } [JsonConstructor] - internal CreditResponse(CreditResponseObject creditResponseObject) + public CreditResponse(CreditResponseObject creditResponseObject) { CreditId = creditResponseObject.CreditId; } diff --git a/src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj b/src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj index cc4763be..f8a547c8 100644 --- a/src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj +++ b/src/Svea.WebPay.SDK/Svea.WebPay.SDK.csproj @@ -23,16 +23,16 @@ - + - + - + diff --git a/src/Svea.WebPay.SDK/SveaHttpClient.cs b/src/Svea.WebPay.SDK/SveaHttpClient.cs index 12142a23..7120cb6e 100644 --- a/src/Svea.WebPay.SDK/SveaHttpClient.cs +++ b/src/Svea.WebPay.SDK/SveaHttpClient.cs @@ -2,7 +2,6 @@ using Svea.WebPay.SDK.Exceptions; using Svea.WebPay.SDK.Json; -using Svea.WebPay.SDK.PaymentAdminApi.Request; using Svea.WebPay.SDK.PaymentAdminApi.Response; using System; @@ -20,7 +19,6 @@ namespace Svea.WebPay.SDK { using Svea.WebPay.SDK.PaymentAdminApi; - using System.Diagnostics; public class SveaHttpClient : ISveaHttpClient { @@ -153,18 +151,18 @@ private async Task ExecuteResourceRequest(response.ResourceUri, configureAwait).ConfigureAwait(configureAwait); - - } while (taskResponse.Status == "InProgress" && taskResponse.ResourceUri == null && polling); + } + while (taskResponse.Status == "InProgress" && taskResponse.ResourceUri == null && polling); response.ResourceUri = taskResponse.ResourceUri; } catch (HttpRequestException e) { - var ex = new HttpRequestException($"Resource object was not returned: {e.Message}"); + var ex = new HttpRequestException($"Resource object was not returned: {e.Message}", inner: e); _logger.LogError(ex, ex.Message); throw ex; @@ -296,4 +294,4 @@ private HttpRequestMessage CreateHttpRequestMessage(HttpMethod httpMethod, Uri u return httpRequestMessage; } } -} \ No newline at end of file +}