Westpac Payment Gateway
// this is the default javascript for a screen layout that will appear in the screen layout javascript editor // // this file can be found at templates/scripts/dialer/defaultScreenLayout.js /** * Implements an interface to westpac's Payway CC system * * You can find a list of test cards here: * https://www.payway.com.au/docs/rest.html#test-card-numbers * * We use the 'payway.js' which essentially injects an iframe with cc UI * into our screen layout. * * We then call into an Action Script SendPaywayPayment to to do the actual * transaction. * * The action script uses this api: * https://www.payway.com.au/docs/rest.html#payway-rest-api * */ $.njDialerPlugin.getPage('${NjWizardPageName}').onPageNext = function(page,defaultNextPage) { return $.njDialerPlugin.getPage("Finished Appointment"); }; $.njDialerPlugin.getPage('${NjWizardPageName}').onPagePrev = function(page,defaultPrevPage) { // do something here debugger; // return page.getPage('Name of desired page'); // to stay on the current page: return page; return defaultPrevPage; }; $.njDialerPlugin.loadjscssfile = function(filename, filetype, callback) { jQuery.getScript(filename, callback); }; /* * this method will be invoked when the lead is displayed for previewing */ $.njDialerPlugin.onPreview = function() { // lead loaded in preview mode debugger; $.njDialerPlugin.loadCCFrame(); }; $.njDialerPlugin.loadCCFrame = function() { // lead connected $.njDialerPlugin.loadjscssfile("https://api.payway.com.au/rest/v1/payway.js", "js", $.njDialerPlugin.createFrame) //dynamically load and add this .js file } $.njDialerPlugin.validAmount = function() { debugger; var valid = false; var amount = $.njDialerPlugin.getFieldValue("CreditCardAmount"); if (amount != undefined && amount.length > 1) { // is this a currency value valid = /^\d{0,4}(\.\d{0,2})?$/.test(amount); } return valid; }; // Callback once script gets loaded. $.njDialerPlugin.createFrame = function(script, textStatus, jqXHR) { debugger; // If the form isn't already loaded then load it. var frames = jQuery.find("#payway-credit-card-iframe0"); if (frames.length == 0) { // Get our submit button var submit = document.getElementById('payway-cc-submit'); submit.disabled = true; // Wire the click handler $("#payway-cc-submit").on('click', $.njDialerPlugin.submitPayment); var testKey = 'XXXX'; var productionKey='YYYYY'; var apiKey = productionKey; payway.createCreditCardFrame({ publishableApiKey: apiKey, onValid: function() { submit.disabled = !$.njDialerPlugin.validAmount(); }, onInvalid: function() { submit.disabled = true; }, tokenMode: "callback" }, $.njDialerPlugin.ccFrameReady); // wire the submit button } } $.njDialerPlugin.ccFrameReady = function(err, frame) { if (err != null) { alert(err.message); } else { $.njDialerPlugin.ccFrame = frame; } }; $.njDialerPlugin.submitPayment = function(event) { // stop the form submission debugger; event.preventDefault(); // Fire the actoin to get the token and the callback will submit the payment. $.njDialerPlugin.ccFrame.getToken($.njDialerPlugin.receiveTokenAndSubmitPayment); }; /** * Callback to called by the above call to ccFrame.getToken * * The data object contains the singlueUseTokenId and the 'amount'.' */ $.njDialerPlugin.receiveTokenAndSubmitPayment = function(err, data) { if (err != null) { alert(err.message); } else { debugger; //var amount = $("#ccAmount").value; // Pass in the single use token. The amount should be in the CreditCardAmount field already. $.njDialerPlugin.setFieldValueByName("paywaySingleUseTokenID", data.singleUseTokenId); // alert("Amount is: " + $.njDialerPlugin.getFieldValue("CreditCardAmount") ); //$.njDialerPlugin.setFieldValueByName("CreditCardAmount", data.amount); $("#progress").text('Credit Card has been submitted please wait...'); $.njDialerPlugin.setFieldValueByName("CreditCardTransactionId", ""); $.njDialerPlugin.setFieldValueByName("CreditCardReceiptNumber", ""); $.njDialerPlugin.setFieldValueByName("CreditCardResponseCode", ""); $.njDialerPlugin.setFieldValueByName("CreditCardStatus", "Processing..."); $.njDialerPlugin.callActionScript('SendPaywayPayment', data.singleUseTokenId, function(result) { try { var allValid = true; var details = JSON.parse(result); // deserialise it. // alert("details:" + details) debugger; $.njDialerPlugin.setFieldValueByName("CreditCardTransactionId", details.transactionId); $.njDialerPlugin.setFieldValueByName("CreditCardReceiptNumber", details.receiptNumber); $.njDialerPlugin.setFieldValueByName("CreditCardResponseCode", details.responseCode); $.njDialerPlugin.setFieldValueByName("CreditCardStatus", details.status); // alert(details.responseCode + ":" + details.status + " " + details.responseText); } catch(e) { alert("something bad in callback" + e); } }); } }; $.njDialerPlugin.paymentFailed = function(xhr, status, error) { alert("failed:" + xhr.responseText); } $.njDialerPlugin.paymentSuccess = function(data) { $.njDialerPlugin.setFieldValueByName("paywaySingleUseTokenID", data.singleUseTokenId); alert("You must click 'Next' to transition to the Final Page for the appointment to be applied"); alert("success:" + data); } /* * this method is called before saving. * also for the contact hub when, hanging up and also when the 'Verify' button is clicked. * * if you return false the save or hangup will fail * * you should display any messages to the user so they know how to resolve the problem... * for example: * alert('The comments field may not be blank'); * */ $.njDialerPlugin.onVerify = function(disposition) { // validate lead and return true if ok to save return true; }; $.njDialerPlugin.onSave = function(disposition) { // lead is being saved, an opportunity to notify external services }; /* * this method will be called when the call is hung up */ $.njDialerPlugin.onHangup = function() { // do something here }; $.njDialerPlugin.getPage('${NjWizardPageName}').onPageEntry = function(page) { // do something here $.njDialerPlugin.loadCCFrame(); };
Payway Action Script
package creditcardpayment; import au.com.noojee.actionscript.api.v1.BaseScreenLayoutAction; import au.com.noojee.actionscript.api.JSCallback; import au.com.noojee.actionscript.api.v1.ScreenLayoutAction; import java.io.IOException; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class SendPaywayPayment extends BaseScreenLayoutAction { static private String TEST_API_KEY = "TTTT"; static private String TEST_MERCHANT_ID = "TEST"; static private String PRODUCTION_API_KEY = "PPPP"; static private String PRODUCTION_MERCHANT_ID = "XXXXX"; static private boolean productionMode = true; static private String REST_END_POINT_URL = "https://api.payway.com.au/rest/v1/transactions"; // @Override public void exec(String singleUseTokenID, Map<String, String> fieldValues, JSCallback jsCallback) throws Exception { logger.warn(singleUseTokenID); //String singleUseTokenID = fieldValues.get("paywaySingleUseTokenID"); String amount = fieldValues.get("CreditCardAmount"); logger.error("Submitting CC Payment: token=" + singleUseTokenID + " amount:" + amount); PaymentTransaction.Response result = submitPayment(singleUseTokenID, amount, fieldValues.get("njLeadId")); //cancelPayment(singleUseTokenID, amount, fieldValues.get("njLeadId")); logger.error("Payment response: " + result); String jsonEntity = new Gson().toJson(result); // jsCallback.call(result); logger.error("Calling jsoncallback with: " + jsonEntity); jsCallback.call(jsonEntity); } public PaymentTransaction.Response submitPayment(String singleUseTokenID, String amount, String leadId) throws PaymentTransactionException, IOException, HTTPException { Map<String,String> args = new HashMap<>(); args.put("singleUseTokenId", singleUseTokenID); args.put("customerNumber", leadId); args.put("transactionType", "payment"); args.put("principalAmount", amount); args.put("currency", "aud"); args.put("orderNumber", leadId); args.put("merchantId", (productionMode == true ? PRODUCTION_MERCHANT_ID : TEST_MERCHANT_ID)); logger.error("args" + Arrays.toString(args.entrySet().toArray())); Map<String, String> headers = getAuthHeaders(); HTTPConnector connector = new HTTPConnector(); URL url = new URL(REST_END_POINT_URL); logger.error("URL:" + url); //PaymentTransaction transaction = new PaymentTransaction(singleUseTokenID, amount); // PaymentTransaction.Response HTTPResponse httpResponse = connector.push(HTTPMethod.POST, url, connector.buildBody(args) , headers, PaymentTransaction.Response.class); logger.error("Raw response: " + httpResponse); //String jsonEntity = new Gson().toJson(response); // return response.getResponseBody(); return httpResponse.getResponse(PaymentTransaction.Response.class); } private Map<String, String> getAuthHeaders() { Map<String,String> headers = new HashMap<>(); String apiKey = (productionMode == true ? PRODUCTION_API_KEY : TEST_API_KEY); String encodedKey = javax.xml.bind.DatatypeConverter.printBase64Binary(apiKey.getBytes()); headers.put("Authorization", "Basic " + encodedKey); return headers; } public PaymentTransaction.Response cancelPayment(String originalTransactionId, String amount, String leadId) throws PaymentTransactionException, IOException, HTTPException { Map<String,String> args = new HashMap<>(); args.put("transactionType", "refund"); args.put("principalAmount", amount); args.put("orderNumber", leadId); args.put("parentTransactionId", originalTransactionId); Map<String, String> headers = getAuthHeaders(); HTTPConnector connector = new HTTPConnector(); URL url = new URL(REST_END_POINT_URL); HTTPResponse httpResponse = connector.push(HTTPMethod.POST, url, connector.buildBody(args) , headers, PaymentTransaction.Response.class); return httpResponse.getResponse(PaymentTransaction.Response.class); } class PaymentTransactionErrors { String message; Link[] links; String getMessage() { return message; } @Override public String toString() { return "PaymentTransactionErrors [message=" + message + ", links=" + Arrays.toString(links) + "]"; } } static class PaymentTransaction { String singleUseTokenID; String principalAmount; PaymentTransaction(String singleUseTokenID, String principalAmount) { this.singleUseTokenID = singleUseTokenID; this.principalAmount = principalAmount; } static class Response { String transactionId; String receiptNumber; String status; // approved String responseCode; String responseText; String customerName; String principleAmount; String surchargeAmount; String paymentAmount; String transactionDateTime; String settlementDate; } } public PaymentTransactionErrors getError(String body) { logger.error("Body" + body); Gson gson = new GsonBuilder().create(); PaymentTransactionErrors errors = gson.fromJson(body, PaymentTransactionErrors.class); return errors; } static class Link { String rel; String href; @Override public String toString() { return "Link [rel=" + rel + ", href=" + href + "]"; } } }