Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Amazon FPS (WinForms, ASP.NET, and C#)

0.00/5 (No votes)
21 Dec 2007 1  
Using Amazon FPS as an alternate to Paypal and Google Checkout.

Introduction

Amazon Web Services is a set of services that developers can use to build applications that have good performance, scalability, and are cost-effective. Amazon FPS (Flexible Payments Service) Limited Beta is one of these Web Services that has been built specifically for developers. It allows transfer of money between any two entities, and is built on top of Amazon's payment infrastructure. The sample application in this article is an example of a small store that implements Amazon FPS in C#. Rather than the traditional credit card signature for payment, users authorize payment by signing into their Amazon account.

Background

Amazon Flexible Payments Service (FPS) Limited Beta is an online payment method that has been built specifically for developers. It is built on top of Amazon's reliable and scalable payment infrastructure. It supports payment methods such as credit cards, bank account debits, and Amazon Balance Payment transfers. There is a different fee associated with each of these payment methods. The sender and recipient of funds declare payment instructions initially. Amazon FPS also allows micro-payments, so that many transactions could be accumulated into a single transaction. And unlike credit cards, transaction amounts can be very minimal. When a lot of single transactions are aggregated into a single transaction, then transaction processing costs can be saved. The sender of funds as such does not pay any transaction fees. For the receiver, however, the transaction processing fees are charged according to whether it is by credit card, bank account debit, or Amazon's Balance Payment transfer method. Existing Amazon.com customers can use their login details with Amazon Payments when buying anything from a site that is powered by Amazon Payments. This way, they would not have to key in all the payment information every time they buy something, as is the case with credit cards.

What you need to do to use this application

I. Register for an Amazon Web Services Developer Account

First, go to this link: https://aws-portal.amazon.com/gp/aws/developer/registration/index.html?.

  1. You must follow the first three steps to create an Amazon.com account, as it is explained. After filling in all the details and successfully creating an account, an email will be sent to your account with a confirmation link, and the steps need to be followed as mentioned in the email. After clicking on the link, you will be redirected to the Amazon.com website home page. And the account details are shown as below:
  2. Click the Your Web Services Account tab on top, and then click Access Identifiers, to see your AWS Access Key ID and Secret Access Key, as shown below:
  3. Your access key ID and secret access key will be shown as below:
  4. Please click the Show link to view your secret access key.

  5. Now, click on the Create New tab under X.509 certificate to create a new certificate. The following screen will show:
  6. Click on the Yes tab to create a new certificate, and the following screen will appear:

Download the private key file and the X.509 certificate. The files will be in .PEM format, and then save both the files in the same folder. The files need to be encrypted using OpenSSL to be encrypted in the P12 format. So to download OpenSSL, go to http://www.openssl.org and download the latest version of OpenSSL and extract the contents of the .tar file to be in the same folder as the saved private key file and the saved certificate file. And then, open the Command Prompt and enter this command:

openssl pkcs12 -export -inkey your_private_key_file.pem 
  -in your_X.509_certificate_file.pem 
  -name "YOUR KeyStorePassword" -out afps-sdk-keystore.p12

Here, your_private_key_file and your_X.509_certificate_file are the names of the private key and the certificate that you downloaded. Now, go to app.config.

<!-- AWS Access Credentials -->
<add key="AWSAccessKeyID" value="YOUR AWSAccessKeyID" />
<add key="AWSSecretAccessKey" value="YOUR AWSSecretAccessKey" />
<!-- Keystore -->
<add key="KeyStoreLocation" value="YOUR afps-sdk-keystore.p12 Location" />
<add key="KeyStorePassword" value="YOUR KeyStorePassword" />

II. Sign up for an Amazon FPS Sandbox Environment

To create an Amazon Payments Sandbox account, go to this link: https://aws-portal.amazon.com/gp/aws/payments/sandbox/index.htm.

Sign in using the Amazon.com account that you created previously. And an account is created for you.

III. Install Visual Studio 2005

IV. Install ASP.NET 2.0

V. Install Web Services Enhancement 3.0

VI. Make sure IIS is installed on your computer

For further details, please visit: http://developer.amazonwebservices.com/connect/entry.jspa?entryID=913&ref=featured.

How the application works

The following section show, with the help of sequence diagrams, the step-by-step process of how the application works:

Setting up caller and recipient tokens

The above diagram shows the process of installing a caller and a recipient token. The AmazonFPSUI class, which is the user interface class, tells the AmazonFPSManager class that the caller and recipient tokens need to be set up. The AmazonFPSManager class then sends a web request in the InstallPaymentInstruction method, which:

  1. establishes the token required for Web Service calls, and
  2. establishes the payment instructions by indicating as either "caller" or "recipient".

In this code sample, the store plays the role of the caller. After that, the recipient token needs to be setup in order to receive payments. In this case, the store is also the recipient. The recipient token is set up using the InstallRecipientInstruction method. This is saved in the application configuration file (app.config). The following source code shows an example:

/* AmazonFPSManager.cs */

static class AmazonFPSManager
{
#region Propertie

    private static AmazonFPSInfo m_AccountFPSInfo = null;

    private static AmazonFPSInfo AccountFPSInfo
    {
        get
        {
            if (m_AccountFPSInfo == null)
            {
                m_AccountFPSInfo = new AmazonFPSInfo();

                m_AccountFPSInfo.CallerToken = 
                   ComfigurationUtillity.GetAppSettings("CallerTokenID");
                m_AccountFPSInfo.RecipientToken = 
                   ComfigurationUtillity.GetAppSettings("RecipientTokenID");
                m_AccountFPSInfo.AWSAccessKeyID = 
                   ComfigurationUtillity.GetAppSettings("AWSAccessKeyID");
                m_AccountFPSInfo.AWSSecretAccessKey = 
                   ComfigurationUtillity.GetAppSettings("AWSSecretAccessKey");
                m_AccountFPSInfo.RecipientName = Settings.TOKENFRIENDLYNAME;
            }

            return m_AccountFPSInfo;
        }
    }
    #endregion

    #region Setup

    public static AmazonFPSInfo Setup(string keyStoreLocation, string keyStorePassword)
    {
        string callerToken = InstallPaymentInstruction(Roles.Caller, 
                             keyStoreLocation, keyStorePassword);
        string recipientToken = InstallPaymentInstruction(Roles.Recipient, 
                                keyStoreLocation, keyStorePassword);

        AmazonFPSInfo retInfo = new AmazonFPSInfo();
        AccountFPSInfo.CallerToken = callerToken;
        AccountFPSInfo.RecipientToken = recipientToken;

        return retInfo;
    }

    private static string InstallPaymentInstruction(Roles role, 
                          string keyStoreLocation, string keyStorePassword)
    {
        InstallPaymentInstructionRequest paymentReq = 
                           new InstallPaymentInstructionRequest();

        paymentReq.TokenType = TokenType.Unrestricted;
        paymentReq.TokenFriendlyName = Settings.TOKENFRIENDLYNAME;
        paymentReq.CallerReference = Settings.CallerReference;

        if (role.Equals(Roles.Caller))
            paymentReq.PaymentInstruction = "MyRole == 'Caller';";
        else
            paymentReq.PaymentInstruction = "MyRole == 'Recipient';";

        AmazonFPSWse clientAgent = 
          AmazonFPSProxy.GetInstance(ComfigurationUtillity.GetAppSettings(
          "WebserviceEndpoint"), keyStoreLocation, keyStorePassword);

        InstallPaymentInstructionResponse paymentRes = 
            //clientAgent.InstallPaymentInstruction(paymentReq);

        if (paymentRes == null)
        {
            throw new ApplicationException("InstallPaymentInstruction Call failed");
        }
        else if (paymentRes.Status == ResponseStatus.Failure)
        {
            ApplicationException ex = 
               new ApplicationException("Caller Token Creation Failed.");

            ex.Data.Add("Is Retriable", paymentRes.Errors[0].IsRetriable);
            ex.Data.Add("Type", paymentRes.Errors[0].ErrorType);
            ex.Data.Add("Code", paymentRes.Errors[0].ErrorCode);
            ex.Data.Add("Reason", paymentRes.Errors[0].ReasonText);

            throw ex;
        }

        return paymentRes.TokenId;
    }
    #endregion

/* ComfigurationUtility.cs */

class ComfigurationUtillity
{
    public static void UpdateAppSettings(string key, string value)
    {
        Configuration config = 
          ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        config.AppSettings.Settings[key].Value = value;
        config.Save(ConfigurationSaveMode.Modified);
        ConfigurationManager.RefreshSection("appSettings");
    }
    public static string GetAppSettings(string key)
    {
        System.Configuration.Configuration config = 
          //ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        return config.AppSettings.Settings[key].Value;
    }
}

Signing in process of sender

The above diagram shows the process of setting up a token for the sender (buyer) when he wants to buy something from the store. AmazonFPSUI sends the sign-in method, with the parameters PaymentReason and PaymentMethod to the AmazonFPSManager. Here, payment reason could be any product that the buyer is buying, and payment method is either by credit card or cash. AmazonFPSManager manages all the transactions that take place. The AMazonFPSManager class sends the caller and the recipient token ID (as set above), the recipient name, and the access key of the recipient, to the AmazonFPSInfo class. The AWS access key is for identifying the recipient. After the recipient is authenticated, a URL is sent back to the User Interface and the user is directed to the Amazon Sign-in page. Here, the user signs in with the Amazon account, and after the user (buyer) is authenticated, the token ID of the buyer (that is, the SenderToken ID) is sent to be stored in the AmazonFPSSenderInfo class. Then, the buyer is redirected back to the website using the signed URL. The following is an example of a signed URL and its parameters:

 /* AmazonFPSManager.cs */

#region SignIn
public static string SignIn(string paymentReason, double amout, string paymentMethod)
{
    string returnUrl = ComfigurationUtillity.GetAppSettings("ReturnUrl");
    string cbuiPipelineUrl = ComfigurationUtillity.GetAppSettings("CBUIPipelineUrl");
    string pipelineName = Pipeline.SINGLE_USE;

    SortedDictionary<string> parameters = new SortedDictionary<string,>();

    parameters.Add("transactionAmount", amout.ToString());
    parameters.Add("callerKey", AccountFPSInfo.AWSAccessKeyID);
    parameters.Add("callerReference", Settings.CallerReference);
    parameters.Add("pipelineName", pipelineName);
    parameters.Add("recipientToken", AccountFPSInfo.RecipientToken);
    parameters.Add("paymentReason", paymentReason);
    parameters.Add("paymentMethod", paymentMethod);
    parameters.Add("returnURL", returnUrl);

    // Build Signature without "awsSignature" oarameter first!
    string signature = 
           BuildSignature(AccountFPSInfo.AWSSecretAccessKey, parameters);

    // Add back to the uri
    parameters.Add("awsSignature", signature);

    return ConstructSignedURL(cbuiPipelineUrl, parameters);
}

public static string ConstructSignedURL(string cbuiPipelineUrl, 
                     SortedDictionary<string, > parameters)
{
    StringBuilder unsignedUrl = new StringBuilder();

    unsignedUrl.AppendFormat("{0}", cbuiPipelineUrl);
    bool first = true;
    foreach (KeyValuePair<string, /> var in parameters)
    {
        unsignedUrl.AppendFormat("{2}{0}={1}", var.Key, 
                    var.Value, (first ? "?" : //"&"));
        first = false;
    }

    return unsignedUrl.ToString();
}

private static string PrepareUrlEncode(string val)
{
    if (HttpUtility.UrlEncode(val).IndexOf('%') != -1)
    {
        String[] strs = HttpUtility.UrlEncode(val.Trim()).Split(new char[] { '%' //});
        StringBuilder retEncodedVAl = new StringBuilder(strs[0]);
        for (int k = 1; k < strs.Length; k++)
        {
            retEncodedVAl.Append('%');
            retEncodedVAl.Append(strs[k].Substring(0, 2).ToUpper() + 
              strs//[k].Substring(2, strs[k].Length - 2));
              // ASCII Key A, B, C, D, E, F to upper case
        }

        return retEncodedVAl.ToString();
    }
    else
    {
        return HttpUtility.UrlEncode(val.Trim());
    }
}

private static string BuildSignature(string secretKey, 
               SortedDictionary<string,> parameters)
{
    StringBuilder sortedParams = 
          new StringBuilder(@"/cobranded//-ui/actions/start");
    bool first = true;
    foreach (KeyValuePair<string, /> var in parameters)
    {
        sortedParams.AppendFormat("{2}{0}={1}", var.Key.Trim(), 
           //PrepareUrlEncode//(var.Value), (first ? "?" : "&"));
        first = false;
    }

    return CalculateHMAC(secretKey, sortedParams.ToString());
}

internal static string CalculateHMAC(String secretKey, String parametersToHash)
{
    ASCIIEncoding ae = new ASCIIEncoding();
    HMACSHA1 signature = new HMACSHA1(ae.GetBytes(secretKey));
    return Convert.ToBase64String(signature.ComputeHash(
                   ae.GetBytes(parametersToHash.ToCharArray())));
}
#endregion

Payment process

The above diagram first shows that after getting the details of the sender token ID from the previous diagram and the email address of the Amazon account of the buyer, a request is then made to the WebRequest class to process the transaction. The results of the transaction are stored in the AmazonPaymentInfo class. The AmzonFPSManager class then checks the payResponse object to see if the transaction was successful. payResponse is an instance of the class PayResponse. PayResponse is a proxy class which defines TransactionResponse, ResponseStatus, ServiceError, and requestIdField. The response is returned accordingly to the browser. The TransactionId is then stored in the AmazonPaymentInfo class.

Pay request

The following code used in the sample application shows the logic behind how a pay request is processed, the various parameters that are involved, and the transaction response details from Amazon FPS:

/* AmazonFPSManager.cs */

#region Pay Method
internal static AmazonPaymentInfo ProcessPayment(
                AmazonFPSSenderInfo amazonFPSSenderInfo, double amountRaw)
{
    PayRequest payRequest = new PayRequest();

    payRequest.CallerTokenId = AccountFPSInfo.CallerToken;
    payRequest.RecipientTokenId = AccountFPSInfo.RecipientToken;
    payRequest.SenderTokenId = amazonFPSSenderInfo.SenderToken;

    Amount amount = new Amount();
    amount.Amount1 = amountRaw.ToString();
    payRequest.TransactionAmount = amount;

    payRequest.ChargeFeeTo = amazonFPSSenderInfo.ChargeFeeTo;
    payRequest.CallerReference = Settings.CallerReference;
    payRequest.TransactionDate = System.DateTime.Now;
    payRequest.CallerDescription = Settings.TOKENFRIENDLYNAME;
    payRequest.RecipientDescription = 
       "Tiny Point of Sale: Amazon Flexible Payments Service (Amazon FPS)";
    payRequest.RecipientReference = "keng@mycosservices.com";
    payRequest.SenderDescription = string.Format("Custome name : {0}, Email address {1}", 
                                   amazonFPSSenderInfo.CustomerName, 
                                   amazonFPSSenderInfo.EmailAddress);
    payRequest.SenderReference = amazonFPSSenderInfo.EmailAddress;

    AmazonFPSWse clientAgent = AmazonFPSProxy.GetInstance();
    PayResponse payResponse = clientAgent.Pay(payRequest);

    TransactionResponse transactionResponse = payResponse.TransactionResponse;

    AmazonPaymentInfo amazonPaymentInfo = new AmazonPaymentInfo();
    amazonPaymentInfo.TransectionId = transactionResponse.TransactionId;
    amazonPaymentInfo.TransectionStatus = transactionResponse.Status.ToString();

    if (payResponse.Status.Equals(ResponseStatus.Success))
    {
        amazonPaymentInfo.IsSuccess = true;
        amazonPaymentInfo.Message = string.Format("Status detail : {0}", 
                                                  transactionResponse.StatusDetail);
    }
    else if (payResponse.Status.Equals(ResponseStatus.Failure))
    {
        amazonPaymentInfo.IsSuccess = false;
        amazonPaymentInfo.Message = string.Format("Error message : {0}", 
                                                  string.Concat(payResponse.Errors));
    }
    return amazonPaymentInfo;
}
#endregion

Using the code

This application is a Windows application. So in order to implement it as an ASP.NET web application, you can download the code (a single class) and have a web form call the class.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here