Amazon Pay

Test Branch

ENGINE-337-Amazon-Pay for sandbox10.

Amazon Pay Setup

Amazon Pay requires a user to login into their Amazon account on the cart page, the user is then redirected back to Core dna and shown the address book and wallet widgets to complete checkout. The shipping address and credit card details are fetched from their logged Amazon account and are not stored on Core dna.

Amazon has its own documentation about how payments are processed and how to integrate here. This guide will only cover what is needed to work with Core dna for a single payment.

To start, You need to create an Amazon Pay account and enable the sandbox / login with Amazon environment. You need to create a new application under Login with Amazon, This is the link which allows your site to connect to Amazon. In the edit details screen, enter the following for  

https://yoursite.com/index.php?action=prodcatalogueorder&form_name=amazon_login

Ensure you point back to amazon_login, this script handles all the Core dna Amazon user login processes and user creation if needed.

Add your site domain into theAllowed JavaScript Origins field. 

Keep note of your Client ID and Client Secret shown here.

You now need to fetch all the keys needed from your sandbox testing environment, in the drop-down select Amazon Pay Sandbox and then select Step 2 - Retrieve credentials

Click the link to retrieve your keys, on page load click the button which Copy your Keys. These are all the keys required to communicate with Core dna and are mapped exactly as they are shown here.

Amazon Pay IPN Setup

When an order is being processed by Amazon, sometimes a card may be declined or some other issue might occur. In this case, Amazon can contact us and mark the order as declined. They can do this by using their IPN (Instant Payment Notification).

Login into Amazon Pay, on the top right click  Settings → Integration Settings. Under notification settings set the following URL;

https://yoursite.com/index.php?action=prodcatalogueorder&form_name=amazon_ipn

The IPN is also used to mark successful orders as closed on the Amazon Pay system.

Core dna Setup


Navigate to the config module (cogwheel → site configuration) and add a new payment method under Amazon Pay. Use all the details set in the previous steps to configure this payment config.

Front End View

All that is needed now is to update the templates with the HTML and JS updates listed below. You can see what the working checkout will look like:

Cart View

Login With Amazon

Shipping Details

Note: it is not required to show the shipping form as the Javascript code will automatically populate the form on success (using the new index 'user' returned from preview). However it is displayed here in order to populate the shipping methods, this was done because the Javascript code was minified and too complex to pull apart to see what function to call. 

Payment Details

Front End Integration/h2>

modules/prodcataloguecart/templates/index.html

JS:
<{* jquery could not be found so include it here *}>
<script type='text/javascript' src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type='text/javascript'>
window.onAmazonLoginReady = function() {
amazon.Login.setClientId('<{amazonClientId}>');
};
window.onAmazonPaymentsReady = function() {
showButton();
};
function showButton(){
var authRequest;
OffAmazonPayments.Button("AmazonPayButton""<{$amazonSellerId}>", {
type: "TYPE",
color: "LightGray",
size: "SIZE",
<{*UPDATE DOMAIN*}>
authorization: function() {
loginOptions = {scope: "profile payments:widget payments:shipping_address",
popup: "POPUP-PARAMETER"};
},
onError: function(error) {
// your error handling code.
alert("The following error occurred: "
+ error.getErrorCode()
' - ' + error.getErrorMessage());
}
});
};
</script>
-----------------------
HTML:<br>
<div id="AmazonPayButton" style="padding-left: 20%;"></div>
<br>

modules/prodcatalogueorder/templates/customer_details.html

JS:
<{* jquery could not be found so include it here *}>
<script type='text/javascript' src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type='text/javascript'>
// get access token
function getURLParameter(name, source) {
return decodeURIComponent((new RegExp('[?|&|#]' + name + '=' +
'([^&;]+?)(&|#|;|$)').exec(source) || [, ""])[1].replace(/\+/g, '%20')) || null;
}
window.onAmazonLoginReady = function() {
amazon.Login.setClientId("<{$amazonClientId}>");
};
window.onAmazonPaymentsReady = function() {
showAddressBookWidget();
};
document.getElementById('Logout').onclick = function() {
amazon.Login.logout();
document.cookie = "amazon_Login_accessToken=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
window.location.href = '/';
};
var orderReferenceId = '';
function showAddressBookWidget() {
// AddressBook
new OffAmazonPayments.Widgets.AddressBook({
sellerId: '<{$amazonSellerId}>',
onReady: function (orderReference) {
orderReferenceId = orderReference.getAmazonOrderReferenceId();
var el;
if ((el = document.getElementById("orderReferenceId"))) {
el.value = orderReferenceId;
}
$('#amazon_order_reference_id').val(orderReferenceId);
},
onAddressSelect: function (orderReference) {
// call the preview on coredna so it can query amazon for the selected address
// and then calculate the shipping costs. You must populate the hidden fields
// for the user data retured.
$.ajax({
dataType: "json",
url: '/index.php?action=prodcatalogueorder&form_name=preview&type=json&getAmazonAddress&orderReferenceId=' + orderReferenceId,
success: function(response) {
if ( response.error ) {
$('#errors').html('<p>'+ message +'</p>');
}
// populate the hidden fields so core dna can process the checkout as normal
$('#delivery_firstname').val(response.user.shipping_address.name);
$('#delivery_lastname').val(response.user.shipping_address.name);
$('#delivery_street_address').val(response.user.shipping_address.address_line1);
$('#delivery_city').val(response.user.shipping_address.city);
$('#delivery_state').val(response.user.shipping_address.state);
$('#delivery_postcode').val(response.user.shipping_address.post_code);
$('#delivery_phone').val(response.user.shipping_address.phone);
// email is not needed as coredna sets the users amazon email as the email address to use
// the email is returned back anyway so you can display it on the site to the user if needed
$('#delivery_email').val(response.user.email);
/**
* This where you remove the shipping options displayed and replace with the those returned from this ajax request.
* There are already JS functions to do this, however I cannot find them.
*/
},
error: function (err) {
$('#errors').html('<p>Something went wrong. Please try again.</p>');
console.log(err);
}
});
},
design: {
designMode: 'responsive'
},
onError: function (error) {
// Error handling code
// We also recommend that you implement an onError handler in your code.
console.log('OffAmazonPayments.Widgets.AddressBook', error.getErrorCode(), error.getErrorMessage());
switch (error.getErrorCode()) {
case 'AddressNotModifiable':
// You cannot modify the shipping address when the order reference is in the given state.
break;
case 'BuyerNotAssociated':
// The buyer is not associated with the given order reference.
// The buyer must sign in before you render the widget.
break;
case 'BuyerSessionExpired':
// The buyer's session with Amazon has expired.
// The buyer must sign in before you render the widget.
break;
case 'InvalidAccountStatus':
// Your merchant account is not in an appropriate state to execute this request.
// For example, it has been suspended or you have not completed registration.
break;
case 'InvalidOrderReferenceId':
// The specified order reference identifier is invalid.
break;
case 'InvalidParameterValue':
// The value assigned to the specified parameter is not valid.
break;
case 'InvalidSellerId':
// The merchant identifier that you have provided is invalid. Specify a valid SellerId.
break;
case 'MissingParameter':
// The specified parameter is missing and must be provided.
break;
case 'PaymentMethodNotModifiable':
// You cannot modify the payment method when the order reference is in the given state.
break;
case 'ReleaseEnvironmentMismatch':
// You have attempted to render a widget in a release environment that does not match the release environment of the Order Reference object.
// The release environment of the widget and the Order Reference object must match.
break;
case 'StaleOrderReference':
// The specified order reference was not confirmed in the allowed time and is now canceled.
// You cannot associate a payment method and an address with a canceled order reference.
break;
case 'UnknownError':
// There was an unknown error in the service.
break;
default:
// Oh My God, What's going on?
}
}
}).bind("addressBookWidgetDiv");
}
</script>
-----------------------
HTML:<input type="hidden" id="amazon_order_reference_id" name="amazon_order_reference_id" value="<{$amazonOrderReferenceId}>" /><div id="AmazonPayButton"></div>
<{*errors from ajax call which sets all the info*}>
<div id="errors"></div>

modules/prodcatalogueorder/templates/payment.html

JS:
<{* jquery could not be found so include it here *}>
<script type="text/javascript"
async></script>
<{* --- AMAZON PAY JS CODE --- *}>
<script type="text/javascript">
window.onAmazonLoginReady = function() {
amazon.Login.setClientId("<{$amazonClientId}>");
};
window.onAmazonPaymentsReady = function() {
showWalletWidget('<{$amazonReferenceOrderId}>');
};
var orderReferenceId = '';
function showWalletWidget(orderReferenceId) {
window.onAmazonPaymentsReady = function() {
showWalletWidget(null);
};
// Wallet
new OffAmazonPayments.Widgets.Wallet({
sellerId: '<{$amazonSellerId}>',
amazonOrderReferenceId: orderReferenceId,
onReady: function(orderReference) {
// this is important!, amazon requires us to pass a new order reference id from payment selection
orderReferenceId = orderReference.getAmazonOrderReferenceId();
var el;
if ((el = document.getElementById("orderReferenceId"))) {
el.value = orderReferenceId;
}
$('#amazon_order_reference_id').val(orderReferenceId);
},
onPaymentSelect: function() {
console.log(arguments);
},
design: {
designMode: 'responsive'
},
onError: function(error) {
// Error handling code
// We also recommend that you implement an onError handler in your code.
console.log('OffAmazonPayments.Widgets.Wallet', error.getErrorCode(), error.getErrorMessage());
}
}).bind("walletWidgetDiv");
}
</script>
<{* --- END AMAZON JS CODE --- *}>
-----------------------
HTML:<{* set the amazon pay details *}>
<{if $amazonLogin}>
<{* set the amazon payment id *}>
<input type="hidden" id="payment-input" name='payment' value=<{$amazonPayGatewayId}>>
<{* this reference id needs to be passed to coredna so we can process the payment *}>
<input type="hidden" id="amazon_order_reference_id" name='amazon_order_reference_id' value="<{$amazonOrderReferenceId}>">
<{* this is the div where the wallet widget will be shown *}>
<div id="walletWidgetDiv" style="height:250px"></div>
<{/if}>
<{* when using amazon pay, do not show the other payment gateways *}>
<{if !$amazonLogin}>
... normal checkout code