Mark Ku's Blog

Amazon pay integration note with csharp - one-time payments

Purpose

Our company's Amazon Pay integration often breaks down, so we need to reconnect/reintegrate it

Prepare

  • Amazon pay api key
  • Setting white list of domains in the Amazon pay backend sytem.
  • Embedding Amaon pay checkout js in Frontend Web site
  • Install Amazon SDK in your backend ( Nuget:Amazon.Pay.API.SDK )

Doc

Amazon pay demo dite

Demo Site

Amazmon pay check out process

  • Create Checkout Session - POST
https://pay-api.amazon.com/:version/checkoutSessions
  • Get Checkout Session - GET
https://pay-api.amazon.com/:version/checkoutSessions/:checkoutSessionId
  • Update Checkout Session - PATCH
https://pay-api.amazon.com/:version/checkoutSessions/:checkoutSessionId
  • Complete Checkout Session - POST
https://pay-api.amazon.com/:version/checkoutSessions/:checkoutSessionId/complete

Step1 Embedding Amaon pay checkout js

<Script src="https://static-na.payments-amazon.com/checkout.js" strategy="afterInteractive"></Script>

Step2 Call our backend api to Create Amazon Session get signature and payload (backend)

private static WebStoreClient InitiateAmazonPayClient() {
         var environment = Projects.InTestingServer ? Environment.Sandbox : Environment.Live;
         var payConfiguration = new ApiConfiguration
         (
             region: Region.UnitedStates,
             environment: environment,
             publicKeyId: AmazonV2.Public_key_id,
             privateKey: AmazonV2.PrivateKey
         );

         var client = new WebStoreClient(payConfiguration);

         return client;
}

public static AmazonCreateCheckoutSession AmazonPayButton(string ChangePaymentReferID) {
         var isChangePayment = !string.IsNullOrEmpty(ChangePaymentReferID);
         var client = InitiateAmazonPayClient();
         var request = new CreateCheckoutSessionRequest(
             checkoutReviewReturnUrl: "http://{your-domain}/carts",
             storeId: Projects.AmazonV2.Store_Id
         );
         request.PaymentDetails.CanHandlePendingAuthorization = false;
         request.DeliverySpecifications.AddressRestrictions.Type = RestrictionType.Allowed;
         request.DeliverySpecifications.AddressRestrictions.AddCountryRestriction("US");
         request.DeliverySpecifications.AddressRestrictions.AddCountryRestriction("CA");

         //generate the button signature
         var signature = client.GenerateButtonSignature(request);
         var payload = request.ToJson();

         return new AmazonCreateCheckoutSession {
            Signature = signature,
            PayloadJSON = payload,
            PublicKeyId = Projects.AmazonV2.Public_key_id,
            MerchantId = Projects.AmazonV2.Merchant_Id,
            IsSandbox = Projects.InTestingServer
         };
      }

Step3 Render Amazon pay button (frontend)

async function amazonPaySetting() {
        const resp = await createAmazonCheckoutSession('');
    
        if (reviewItems) {
            updateCartReviewItems(reviewItems);
        }

        if (resp.Success) {
            const data = resp.Data;
            const request = {
                merchantId: data.MerchantId,
                ledgerCurrency: 'USD',
                checkoutLanguage: 'en_US',
                productType: 'PayAndShip',
                placement: 'Cart',
                buttonColor: 'Gold',
                createCheckoutSessionConfig: {
                    payloadJSON: data.PayloadJSON,
                    signature: data.Signature,
                    publicKeyId: data.PublicKeyId,
                },               
                sandbox: process.env.NODE_ENV === 'development' ? true : false,
            };

            
            (window as any).amazon.Pay.renderButton('#AmazonPayButton', request);

            setTimeout(() => {
                if (amazonBtn.current) {
                    amazonBtn.current.click();
                }
            }, 1000);
        }
    }

Amazon Pay button for express checkout
Amazon Pay button for express checkout

Step 4 When the user clicks 'Express Checkout', they will be redirected to the payment selection page and can continue with the checkout process (frontend)

Amazon Pay sandbox checkout with shipping and Visa payment
Amazon Pay sandbox checkout with shipping and Visa payment

Step5 Amazon will redirect to payload's checkoutReviewReturnUrl with checkout session Id (frontend)

Amazon checkout session ID URL in browser address bar
Amazon checkout session ID URL in browser address bar

Step6 Call our backend API to retrieve the checkout session and obtain Amazon shipping information.(backend)

public static AmazonV2Result GetPaymentInfo(string AmazonCheckoutSessionId) {
         var client = InitiateAmazonPayClient();
         var getInfo = client.GetCheckoutSession(AmazonCheckoutSessionId);
         var result = new AmazonV2Result {
            Success = false
         };

         if (!getInfo.Success) {
            return GetAmazonIssue(getInfo.RawResponse);
         }

         result.Success = true;
         result.CustomerInfo = FilloutCustomerInfo(getInfo.ShippingAddress, getInfo.Buyer.Email);

         return result;
}
Amazon Pay review order screen with shipping and billing addresses
Amazon Pay review order screen with shipping and billing addresses

Step7 Call our backend API to update the checkout session and retrieve the Amazon purchase URL(backend)

Backend will UpdateCheckoutSession then get Checkout Url with Amazon pay SDK.

private static AmazonV2Result UpdateCheckoutSession(string AmazonCheckoutSessionId, AmazonV2Request Data, bool Authorize, string ChangPaymentsReferID) {
         NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

         var result = new AmazonV2Result {
            Success = false,
            AmazonPayRedirectUrl = string.Empty,
            ResponseJson = string.Empty
         };

         try {
            var client = InitiateAmazonPayClient();
            var request = new UpdateCheckoutSessionRequest();
            var isChangPaymentsStep = !string.IsNullOrEmpty(ChangPaymentsReferID);

            request.WebCheckoutDetails.CheckoutResultReturnUrl = isChangPaymentsStep ? ChangePaymentsSuccessLink + ChangPaymentsReferID + "&complete=1" : successLink;
            request.PaymentDetails.ChargeAmount.Amount = Data.OrderAmount;
            request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
            request.PaymentDetails.CanHandlePendingAuthorization = false;
            request.PaymentDetails.PaymentIntent = Authorize ? PaymentIntent.Authorize : PaymentIntent.AuthorizeWithCapture;
            request.MerchantMetadata.MerchantReferenceId = Data.OrderId;
            request.MerchantMetadata.MerchantStoreName = "ibuypower.com";
            request.MerchantMetadata.NoteToBuyer = Data.ModelName;

            // send the request
            var updateResult = client.UpdateCheckoutSession(AmazonCheckoutSessionId, request);

            // check if API call was successful
            if (updateResult.Success) {
               var josn = DataPackager.Json(updateResult);
            result.Success = true;
               result.AmazonPayRedirectUrl = updateResult.WebCheckoutDetails.AmazonPayRedirectUrl;
               result.ResponseJson = JsonConvert.SerializeObject(updateResult.RawResponse, Formatting.Indented);
            }
            else {
               logger.Error($"[AmazonPay][AmazonV2Helper][PurchaseGetAmazonPayRedirectUrl][UpdateCheckoutSession][Error]Request:{DataPackager.Json(request)},Response:{DataPackager.Json(updateResult)}");
               result.Message = "Please select another credit card or change payment method and try again.";
            }
         }
         catch (Exception ex) {
            logger.Error($"[AmazonPay][AmazonV2Helper][PurchaseGetAmazonPayRedirectUrl][UpdateCheckoutSession][Error]{ex.Message},{ex.StackTrace}");
         }

         return result;
      }

Step8 When a member checks out on Amazon's website, they are redirected to our website with an 'amazonCheckoutSessionId.' This allows us to initiate the 'CompleteCheckoutSession' process.

Amazon Pay secure sign-in confirmation in sandbox environment
( return to CheckoutResultReturnUrl)

Step9 CompleteCheckoutSession (backend)

private static AmazonV2Result CompleteCheckoutSession(string AmazonCheckoutSessionId, decimal Amount) {
         NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

         AmazonV2Result result = new AmazonV2Result();

         try {
            var client = InitiateAmazonPayClient();
            var request = new CompleteCheckoutSessionRequest(Amount, Currency.USD);
            CheckoutSessionResponse checkoutSessionResponse = client.CompleteCheckoutSession(AmazonCheckoutSessionId, request);

            logger.Info($"[AmazonPay][Success][CompleteCheckoutSession]AmazonCheckoutSessionId:{AmazonCheckoutSessionId},Amount{Amount},Data:{DataPackager.Json(checkoutSessionResponse)}");

            if (checkoutSessionResponse.Success) {
               result.Success = true;
               result.ChargeId = checkoutSessionResponse.ChargeId;
               result.ChargePermissionId = checkoutSessionResponse.ChargePermissionId;
               result.ResponseJson = JsonConvert.SerializeObject(checkoutSessionResponse.RawResponse, Formatting.Indented);
            }
            else {
               result = GetAmazonIssue(checkoutSessionResponse.RawResponse);

               logger.Error($"[AmazonPay][Error][CompleteCheckoutSession][GetAmazonIssue][Step 3]AmazonCheckoutSessionId:{AmazonCheckoutSessionId},Amount{Amount},Data:{DataPackager.Json(checkoutSessionResponse)}");
            }
         }
         catch (System.Exception ex) {
            logger.Error($"[AmazonPay][Error][CompleteCheckoutSession][Step 3]AmazonCheckoutSessionId:{AmazonCheckoutSessionId},Amount{Amount},Ex:{ex.Message},StackTrace:{ex.StackTrace}");
         }

         return result;
      }

Step 10 Finsih

Amazon pay is a bit complicated, with a lot of page redirections, so it's really difficult to integrate.

Reference

(Error Code)[https://developer.amazon.com/docs/amazon-pay-api-v2/charge.html]

Author

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。Read More

Found this useful?

The author's free tools, daily podcasts and newsletter are all here.

Mark Ku · This article is licensed under CC BY 4.0. Credit the author and link back to the original when reusing it.

Comments

Subscribe to Newsletter

Subscribe to get new posts delivered instantly — never miss a tech share.

By submitting, you agree to receive emails. You can anytime.

Popular Posts

View all
Mark Ku
··602

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution
Mark Ku
··492

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.
Mark Ku
··334

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki
Mark Ku
··268

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning
Mark Ku
··218

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1
Mark Ku
··217

Setting Up Samba on Ubuntu to Share Folders with Windows 11

Setting Up Samba on Ubuntu to Share Folders with Windows 11