Skip to content
This repository has been archived by the owner on Aug 27, 2018. It is now read-only.

Using CFAmazon

ecorgroup edited this page May 10, 2011 · 3 revisions

With Amazon Payments, web developers can simplify the checkout and payment process for millions of people who are already registered Amazon customers. Amazon Payments offers a hosted e-commerce solution and a payments-only service called Amazon Simple Pay, but what do you use when you want to integrate Amazon Payments into your own e-commerce site?

Checkout by Amazon offers an API for developers who want to integrate the service into their own website. The API offers significant flexibility for developers, but there is so much it can sometimes be difficult to know where to start. After reading this, you should have an idea of how to get Checkout by Amazon working on your ColdFusion website.


There are three high level areas every developer should be aware of before getting started.

  1. XML-based Carts & Signatures: All information sent between your website and Amazon is formatted as XML. Security is important, especially when dealing with online payments. Therefore, all XML must be signed with a unique signature. Creating the signature is a part of the process where many questions arise, so they’re addressed in detail below. XML-based carts are covered in the basic integration guide provided on Seller Central (your Amazon merchant account).

  2. Callback API: The Callback API offers developers advanced customization capabilities for the checkout process. This API can be used to programmatically override default taxes, manage promotions, and create special shipping options. It is called the Callback API because it uses callback URLs defined in Seller Central (the merchant portal) to communicate with your website. Details of the callback API are covered in the Callback API PDF guide.

  3. Instant Payment Notification: IPN (IOPN) can be used to perform custom actions for an order. This feature will post information to a predefined URL when an order is placed or cancelled. This is a good way to log orders in your own database, update your CRM application, or create a follow-up request. IPN is covered in the Integration Guide. Working with Amazon Payments requires an account. See the entry on Creating an Amazon Payments Seller Account.


XML-based Shopping Cart

Creating an order is a matter of passing a shopping cart to Amazon via XML. The XML must be signed with a unique key in order for Amazon to consider it valid. The first step is, of course, to create the XML shopping cart. The XML file must validate to the XSD provided by Amazon. The code should look something like the following:

<?xml version=”1.0” encoding=”UTF-8”?>
<Order xmlns=”http://payments.amazon.com/checkout/2009-05-15/”>
  <Cart>
    <Items>
      <Item>
        <SKU>ABC123</SKU>
        <MerchantId>AEIOU1234AEIOU</MerchantId>
        <Title>Red Fish</Title>
        <Price>
          <Amount>19.99</Amount>
          <CurrencyCode>USD</CurrencyCode>
        </Price>
        <Quantity>1</Quantity>
      </Item>
    </Items>
  </Cart>
</Order>

The CFAmazon library provides cart and item CFC objects that can be used to intuitively prepare an order. Creating the XML is a matter of calling the cart.getXml() method.

<cfscript> 
      cart = createObject("component","com.amazon.cba.cart"); 
      cart.init('ACCESSKEY','SECRET','MERCHANTID',true); 
     
      //Add a regular item to the cart. 
      cart.addItem('Red Fish',19.99,1); 
     
      //Add a customized item to the cart.
      item = createObject("component","com.amazon.cba.item");
      item.init('Blue Fish',29.99,1);
      item.setWeight(1.75,"lb");
      cart.addCustomItem(item);
</cfscript>
<cfdump var="#cart.getXml()#" label="Order XML"/>

The code above (found in CFAmazon cba-xml-signed.cfm example) creates an XML cart with two different items, one of which has a custom weight. To see the XML, dump the cart object.

Now that an order is prepared, it must be signed and submitted to Amazon Payments for processing. Amazon provides some standard JavaScript widgets that can be used to generate the Checkout by Amazon button shown below:

Amazon Payments Screenshot

To create the button, the JavaScript file must be included in the head of page:

<script language=“javascript” src="https://payments-sandbox.amazon.com/cba/js/PaymentWidgets.js"></script>

Put a <div> with a unique ID into the page wherever the button should show up. Before the </body> tag, JavaScript must be included to actually create the button.

<body>
<div id="signedBtn"/>
      <cfoutput>
      <script>
            new CBA.Widgets.StandardCheckoutWidget({
                  merchantId:'#cart.merchantID#',
                  orderInput:{
                        format:"XML",
                        value: "type:merchant-signed-order/aws-accesskey/1;order:#toBase64(cart.getXml())#;signature:#cart.getXmlSignature()#;aws-access-key-id:#cart.accessKeyID#"
                  },
                  buttonSettings:{
                        size:'large',
                        color:'orange',
                        background:'green'
                  }
            }).render("signedBtn");
      </script>
      </cfoutput>
</body>

Let’s break down the code. First, the cart object contains all of the parameters required for submitting an order. The merchantID and accessKeyID , highlighted in grey and yellow, are defined when the cart is initialized (these values are found in your Seller Central account). The order (in turquoise) is the Base64 encoding of your XML cart. A signature, highlighted in green, can be generated using the getXmlSignature() method of the cart object.

The code above is enough to generate a checkout button for an order. When the user clicks on the button, a window will pop up asking them to login to Amazon to complete the order.

Amazon Payments Screenshot

Digging Deeper: How is the signature generated?

Generating the signature is typically the most problematic part of the process. CFAmazon abstracts the complexity of creating a signature by hand, but here is a breakdown for those who are interested. The signature is generated using the XML cart (string) and a unique key (your Access Secret assigned in Seller Central) encrypted using HMAC-SHA1. The getXmlSignature() method is a shortcut method that converts the XML cart to a string and passes the data and your Access Secret to a common method found in the factory.cfc file shown below.

<cffunction name="sign" access="public" output="false" returntype="String">
      <cfargument name="data" type="String" required="true"/>
      <cfargument name="key" type="String" required="true"/>
      <cfscript>
            var sformat = "UTF-8";
            var ekey    = createObject("java","javax.crypto.spec.SecretKeySpec");
            var secret = ekey.Init(arguments.key.getBytes(sformat),"HmacSHA1");
            var mac     = createObject("java","javax.crypto.Mac");   
               
            //Initialize the MAC
            mac = mac.getInstance(ekey.getAlgorithm());
            mac.init(secret);
               
            return toBase64(mac.doFinal(arguments.data.getBytes(sformat)));
      </cfscript>
</cffunction>

The function above creates a java object to do the encryption and returns a Base64 encoded string that can be used as the signature.


The Callback API

If you’re running a promotion, have special shipping options, or need to specify taxes for a specific order on the fly, then the callback API is necessary. Use of the callback API requires additional XML code. Once again, CFAmazon attempts to abstract the complexity of creating the XML. Observe the code below (xml-signed-callbacks.cfm):

<cfscript>
      cart = createObject("component","com.amazon.cba.cart");
      cart.init('ACCESSKEY','SECRET','MERCHANTID',true);
     
      //Initialize callbacks
      cart.setCallbackUrl('CALLBACKURL');
      cart.setCalculatePromotions(true);
     
      //Add a regular item to the cart.
      cart.addItem('Red Fish',19.99,2,'12345SKU');
     
      //Add a customized item to the cart.
      item = createObject("component","com.amazon.cba.item");
      item.init('Blue Fish',29.99,1);
      item.setWeight(1.75,"lb");
      item.setSKU('6789SKU');
      cart.addCustomItem(item);
     
      //Add a regular item to the cart.
      cart.addItem('Fish Tank',49.99,1,'SKU123');
</cfscript>

This example sets the callback URL and indicates a custom promotion needs to be calculated and applied to the order. Notice that all of the items are assigned a unique SKU number. SKU numbers act as a unique identifier and are required to use the callback API. When the user clicks the Checkout with Amazon button, Amazon creates a form POST to the callback URL. Your website must be able to handle this request and respond in a timely fashion (under 5 seconds) in order to be considered valid.

CFAmazon includes a file called callback.cfm which contains example code for your callback URL. An object called callback.cfc provides several helper methods to simplify the process of creating a response. Since Amazon uses a POST request to callback URL, all of the data is available in the FORM scope, but it is URL encoded.

<!—- Create an Amazon Factory to help with processing the callback —->
<cfset callback = createObject("component","com.amazon.cba.callback")/>
<cfset callback.init('ACCESSKEY','SECRET','MERCHANTID',true)/>
 
<!—- Callback API Response —->
<cftry>
     
      <!—- 1. Verify the request is from Amazon. —->
      <cfset valid = callback.verifyRequestIsFromAmazon(urldecode(form.UUID),urldecode(form.Timestamp),form.Signature)/>
      <cfif not valid>
            <!—- Handle fake requests —->
            <cfexit>
      </cfif>
     
     
      <!—- 2. Parse the XML Request Data —->
      <cfset callback.parseRequest(urldecode(form['order-calculations-request']))/>
     
     
      <!—- 3. Calculate Promotional Discounts —->
      <cfscript>
            //Get all Item SKU numbers from the order
            skus = callback.getAllItemSkuNumbers();
                       
            //Create & apply a basic promotion/discount
            callback.addPromotion("halfoffbluefish","Half Off Blue Fish!",.5,false);
            callback.applyPromotion(skus[2],"halfoffbluefish"); //applied to the second item (Blue Fish)
           
      </cfscript>
     
      <cfoutput>#callback.generateResponse()#</cfoutput>   
     
      <cfcatch type="any">
            <!—- Respond to Amazon with an error —->
            <cfoutput>#toString(callback.getXmlResponse("INTERNAL_SERVER_ERROR",cfcatch.message&" "&cfcatch.detail))#/cfoutput>
      </cfcatch>
</cftry>
  1. Since your callback URL is open to the whole world, it is important to verify that the callback request is actually from Amazon. The verifyRequestIsFromAmazon method (found in factory.cfc) is used to accomplish this. This function generates a valid signature and compares it to the signature submitted to your callback page. Additionally, it checks the timestamp against your local server time to make sure the request was submitted within the last 15 minutes. If either of these conditions fails, the request is not considered valid and processing cannot continue.

  2. Once the request is validated, the callback can processed. Amazon posts an attribute called order-calculations-request containing the XML cart with additional nodes (such as customer address). CFAmazon parses this content into an attribute (struct) called REQUEST, simply to make the data more manageable and accessible.

  3. Next, the custom promotion must be defined and applied to the items you want to discount. The addPromotion method defines a unique ID for the promotion, a description, and a fixed or percentage-based discount value. The addPromotion method associates the promotion with the SKU number of the discounted item. Both of these methods set flags that help the callback object generate a proper XML response with only the requested callback information.

Finally, the XML response is generated and output to the page. Amazon uses this to complete the order process with the specified customizations applied in real time.

It’s important to note that the callback API is very flexible and very powerful. CFAmazon only implements the most common functionality. If your website requires more customization, read the Callback API Guide (PDF) provided by Amazon. Once you have gained an understanding of how callbacks are processed, the callback.cfc file should be updated with your modifications. If you implement any additional functionality, please consider contributing it to the CFAmazon project!


Instant Payment Notification

IPN provides developers the ability to direct the user’s browser to different locations after they complete or cancel the order. Amazon automatically sends a confirmation email to the customer upon completion of a transaction, but IPN can come in handy if you want to setup a “Thank You” landing page, log the order, or perform any other post-order processing. Amazon will direct the user’s browser to the appropriate URL defined in your Seller Central “Checkout Pipeline” settings shown below. This is a basic redirect that contains a number of URL parameters. For testing purposes, it’s a good idea to simply dump the URL scope to the screen (i.e. <cfdump var="#url#">) in order to see all of the data available.

Amazon Payments IOPN


Conclusion

CFAmazon is really just a starting point for working with Amazon Payments. There are many more features available for developers. Keep an eye on the GitHub repository and RIAForge for updates, and of course submit your own modifications for the benefit of the community!

The full blog entry is available at http://www.coreybutler.com/post/2834602991/using-coldfusion-with-amazon-payments

Clone this wiki locally