Apple Pay iFields Integration
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<!-- Use the following src for the script on your form and replace ****version**** with the desired version: src="https://cdn.cardknox.com/ifields/****version****/ifields.min.js" -->
<script src="ifields.min.js"></script>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
ckApplePay.enableApplePay({
initFunction: 'apRequest.initAP',
amountField: 'amount'
});
});
const apRequest = {
buttonOptions: {
buttonContainer: "ap-container",
buttonColor: APButtonColor.black,
buttonType: APButtonType.pay
},
totalAmount: null,
taxAmt: null,
shippingMethod: null,
creditType: null,
getTransactionInfo: function (taxAmt, shippingMethod, creditType) {
try {
this.shippingMethod = shippingMethod || this.shippingMethod || {
"label": "Free Shipping",
"amount": "0.00",
"type": "final"
};
this.taxAmt = roundToNumber(taxAmt, 4) || this.taxAmt || 0.07;
this.creditType = creditType || this.creditType;
const amt = getAmount();
const lineItems = [
{
"label": "Subtotal",
"type": "final",
"amount": amt
},
this.shippingMethod
];
if (this.creditType === "credit") {
lineItems.push({
"label": "Credit Card Fee",
"amount": roundTo(0.0275*amt, 2),
"type": "final"
});
}
lineItems.push({
"label": "Estimated Tax",
"amount": roundTo(this.taxAmt*amt, 2),
"type": "final"
});
let totalAmt = 0;
lineItems.forEach((item) => {
totalAmt += parseFloat(item.amount)||0;
});
totalAmt = roundTo(totalAmt, 2);
this.totalAmount = totalAmt;
return {
'lineItems': lineItems,
total: {
type: 'final',
label: 'Total',
amount: totalAmt,
}
};
} catch (err) {
console.error("getTransactionInfo error ", exMsg(err));
}
},
onGetTransactionInfo: function () {
try {
return this.getTransactionInfo();
} catch (err) {
console.error("onGetTransactionInfo error ", exMsg(err));
}
},
onGetShippingMethods: function() {
return [
{
label: 'Free Shipping',
amount: '0.00',
identifier: 'free',
detail: 'Delivers in five business days',
},
{
label: 'Express Shipping',
amount: '5.00',
identifier: 'express',
detail: 'Delivers in two business days',
},
];
},
onShippingContactSelected: function(shippingContact) {
const self = this;
return new Promise((resolve, reject) => {
try {
console.log("shippingContact", JSON.stringify(shippingContact));
let taxAmt = 0.1;
const newShippingMethods = [
{
label: 'Free Shipping',
amount: '0.00',
identifier: 'free',
detail: 'Delivers in five business days',
}
];
if (shippingContact && shippingContact.administrativeArea) {
if (shippingContact.administrativeArea === "NY") {
taxAmt = 0.0875;
newShippingMethods.push(
{
label: 'Overnight Shipping',
amount: '10.00',
identifier: 'overnight',
detail: 'Delivers in one business days',
}
);
} else if (shippingContact.administrativeArea === "NJ") {
taxAmt = 0.07;
newShippingMethods.push(
{
label: 'Express Shipping',
amount: '5.00',
identifier: 'express',
detail: 'Delivers in two business days',
}
);
}
}
const resp = self.getTransactionInfo(taxAmt, newShippingMethods[0]);
resp.shippingMethods = newShippingMethods;
resolve(resp);
} catch (err) {
const apErr = {
code: "-101",
contactField: "",
message: exMsg(err)
}
console.error("onShippingContactSelected error.", exMsg(err));
reject({errors: [err]});
}
})
},
onShippingMethodSelected: function(shippingMethod) {
const self = this;
return new Promise(function (resolve, reject) {
try {
console.log("shippingMethod", JSON.stringify(shippingMethod));
const resp = self.getTransactionInfo(null, shippingMethod);
resolve(resp);
} catch (err) {
const apErr = {
code: "-102",
contactField: "",
message: exMsg(err)
}
console.error("onShippingMethodSelected error.", exMsg(err));
reject({errors: [err]});
}
})
},
onPaymentMethodSelected: function(paymentMethod) {
const self = this;
return new Promise((resolve, reject) => {
try {
console.log("paymentMethod", JSON.stringify(paymentMethod));
const resp = self.getTransactionInfo(null, null, paymentMethod.type);
resolve(resp);
} catch (err) {
const apErr = {
code: "-102",
contactField: "",
message: exMsg(err)
}
console.error("onPaymentMethodSelected error.", exMsg(err));
reject({errors: [err]});
}
})
},
validateApplePayMerchant: function () {
return new Promise((resolve, reject) => {
try {
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://api.cardknox.com/applepay/validate");
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
console.log("validateApplePayMerchant", JSON.stringify(xhr.response));
resolve(xhr.response);
} else {
console.error("validateApplePayMerchant", JSON.stringify(xhr.response), this.status);
reject({
status: this.status,
statusText: xhr.response
});
}
};
xhr.onerror = function () {
console.error("validateApplePayMerchant", xhr.statusText, this.status);
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send();
} catch (err) {
setTimeout(function () { console.log("getApplePaySession error: " + exMsg(err)) }, 100);
}
});
},
onValidateMerchant: function() {
return new Promise((resolve, reject) => {
try {
this.validateApplePayMerchant()
.then((response) => {
try {
console.log(response);
resolve(response);
} catch (err) {
console.error("validateApplePayMerchant exception.", JSON.stringify(err));
reject(err);
}
})
.catch((err) => {
console.error("validateApplePayMerchant error.", JSON.stringify(err));
reject(err);
});
} catch (err) {
console.error("onValidateMerchant error.", JSON.stringify(err));
reject(err);
}
});
},
authorize: function(applePayload, totalAmount) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://<your domain>/<path to handle authorization>");
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
const data = {
amount: totalAmount,
payload: applePayload
};
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(data));
});
},
onPaymentAuthorize: function(applePayload) {
return new Promise((resolve, reject) => {
try {
this.authorize(applePayload, this.totalAmount)
.then((response) => {
try {
console.log(response);
const resp = JSON.parse(response);
if (!resp)
throw "Invalid response: "+ response;
if (resp.xError) {
throw resp;
}
resolve(response);
} catch (err) {
throw err;
// reject(err);
}
})
.catch((err) => {
console.error("authorizeAPay error.", JSON.stringify(err));
apRequest.handleAPError(err);
reject(err);
});
} catch (err) {
console.error("onPaymentAuthorize error.", JSON.stringify(err));
apRequest.handleAPError(err);
reject(err);
}
});
},
onPaymentComplete: function(paymentComplete) {
if (paymentComplete.response) { //Success
const resp = JSON.parse(paymentComplete.response);
if (resp.xRefNum) {
setAPPayload("Thank you for your order:("+resp.xRefNum+")");
} else {
setAPPayload("Thank you for your order.");
}
} else if (paymentComplete.error) {
console.error("onPaymentComplete", exMsg(paymentComplete.error));
handleAPError(paymentComplete.error);
}
},
handleAPError: function(err) {
if (err && err.xRefNum) {
setAPPayload("There was a problem with your order:("+err.xRefNum+")");
} else {
setAPPayload("There was a problem with your order:"+exMsg(err));
}
},
initAP: function() {
return {
buttonOptions: this.buttonOptions,
merchantIdentifier: "<Your Apple Merchant ID>",
requiredBillingContactFields: ['postalAddress', 'name', 'phone', 'email'],
requiredShippingContactFields: ['postalAddress', 'name', 'phone', 'email'],
onGetTransactionInfo: "apRequest.onGetTransactionInfo",
onGetShippingMethods: "apRequest.onGetShippingMethods",
onShippingContactSelected: "apRequest.onShippingContactSelected",
onShippingMethodSelected: "apRequest.onShippingMethodSelected",
onPaymentMethodSelected: "apRequest.onPaymentMethodSelected",
onValidateMerchant: "apRequest.onValidateMerchant",
onPaymentAuthorize: "apRequest.onPaymentAuthorize",
onPaymentComplete: "apRequest.onPaymentComplete",
onAPButtonLoaded: "apRequest.apButtonLoaded",
isDebug: true
};
},
apButtonLoaded: function(resp) {
if (!resp) return;
if (resp.status === iStatus.success) {
showHide(this.buttonOptions.buttonContainer, true);
showHide("lbAPPayload", true);
} else if (resp.reason) {
console.log(resp.reason);
}
}
};
function setAPPayload(value) {
const apTxt = document.getElementById('ap-payload');
apTxt.value = value;
showHide(apTxt, value);
}
function showHide(elem, toShow) {
if (typeof(elem) === "string") {
elem = document.getElementById(elem);
}
if (elem) {
toShow ? elem.classList.remove("hidden") : elem.classList.add("hidden");
}
}
function getAmount () {
return roundToNumber(document.getElementById("amount").value || "0", 2);
}
</script>
<style>
body {
margin: 10px;
}
div.main {
width: 350px;
}
.ap {
border: 0;
width: 250px;
height: auto;
min-height: 55px;
padding: 0px;
margin-bottom: 12px;
}
input {
border: 1px solid black;
font-size: 14px;
padding: 3px;
width: 250px;
margin-bottom: 12px;
}
.hidden {
display: none;
}
textarea {
border: 1px solid black;
width: 100%;
}
</style>
</head>
<body>
<div class="main">
<form id="payment-form" method="POST">
<input id="amount" name="xAmount" placeholder="Amount" type="number" inputmode="decimal"></input>
<br/>
<div id="ap-container" class="ap hidden" >
<br/>
</div>
<br/>
<label id="lbAPPayload" class="hidden">Apple Pay Payload: </label>
<br />
<textarea id="ap-payload" class="hidden" rows="10" readonly="true"></textarea>
<br/>
</form>
</div>
</body>
</html>
Last updated