Apple Pay iFields Integration
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
5
<meta content="utf-8" http-equiv="encoding">
6
​
7
<!-- 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" -->
8
<script src="ifields.min.js"></script>
9
<script type="text/javascript">
10
document.addEventListener("DOMContentLoaded", function(event) {
11
ckApplePay.enableApplePay({
12
initFunction: 'apRequest.initAP',
13
amountField: 'amount'
14
});
15
});
16
​
17
const apRequest = {
18
buttonOptions: {
19
buttonContainer: "ap-container",
20
buttonColor: APButtonColor.black,
21
buttonType: APButtonType.pay
22
},
23
totalAmount: null,
24
taxAmt: null,
25
shippingMethod: null,
26
creditType: null,
27
getTransactionInfo: function (taxAmt, shippingMethod, creditType) {
28
try {
29
this.shippingMethod = shippingMethod || this.shippingMethod || {
30
"label": "Free Shipping",
31
"amount": "0.00",
32
"type": "final"
33
};
34
this.taxAmt = roundToNumber(taxAmt, 4) || this.taxAmt || 0.07;
35
this.creditType = creditType || this.creditType;
36
const amt = getAmount();
37
const lineItems = [
38
{
39
"label": "Subtotal",
40
"type": "final",
41
"amount": amt
42
},
43
this.shippingMethod
44
];
45
if (this.creditType === "credit") {
46
lineItems.push({
47
"label": "Credit Card Fee",
48
"amount": roundTo(0.0275*amt, 2),
49
"type": "final"
50
});
51
}
52
lineItems.push({
53
"label": "Estimated Tax",
54
"amount": roundTo(this.taxAmt*amt, 2),
55
"type": "final"
56
});
57
let totalAmt = 0;
58
lineItems.forEach((item) => {
59
totalAmt += parseFloat(item.amount)||0;
60
});
61
totalAmt = roundTo(totalAmt, 2);
62
this.totalAmount = totalAmt;
63
64
return {
65
'lineItems': lineItems,
66
total: {
67
type: 'final',
68
label: 'Total',
69
amount: totalAmt,
70
}
71
};
72
} catch (err) {
73
console.error("getTransactionInfo error ", exMsg(err));
74
}
75
},
76
onGetTransactionInfo: function () {
77
try {
78
return this.getTransactionInfo();
79
} catch (err) {
80
console.error("onGetTransactionInfo error ", exMsg(err));
81
}
82
},
83
onGetShippingMethods: function() {
84
return [
85
{
86
label: 'Free Shipping',
87
amount: '0.00',
88
identifier: 'free',
89
detail: 'Delivers in five business days',
90
},
91
{
92
label: 'Express Shipping',
93
amount: '5.00',
94
identifier: 'express',
95
detail: 'Delivers in two business days',
96
},
97
];
98
},
99
onShippingContactSelected: function(shippingContact) {
100
const self = this;
101
return new Promise((resolve, reject) => {
102
try {
103
console.log("shippingContact", JSON.stringify(shippingContact));
104
let taxAmt = 0.1;
105
const newShippingMethods = [
106
{
107
label: 'Free Shipping',
108
amount: '0.00',
109
identifier: 'free',
110
detail: 'Delivers in five business days',
111
}
112
];
113
if (shippingContact && shippingContact.administrativeArea) {
114
if (shippingContact.administrativeArea === "NY") {
115
taxAmt = 0.0875;
116
newShippingMethods.push(
117
{
118
label: 'Overnight Shipping',
119
amount: '10.00',
120
identifier: 'overnight',
121
detail: 'Delivers in one business days',
122
}
123
);
124
} else if (shippingContact.administrativeArea === "NJ") {
125
taxAmt = 0.07;
126
newShippingMethods.push(
127
{
128
label: 'Express Shipping',
129
amount: '5.00',
130
identifier: 'express',
131
detail: 'Delivers in two business days',
132
}
133
);
134
}
135
}
136
const resp = self.getTransactionInfo(taxAmt, newShippingMethods[0]);
137
resp.shippingMethods = newShippingMethods;
138
resolve(resp);
139
} catch (err) {
140
const apErr = {
141
code: "-101",
142
contactField: "",
143
message: exMsg(err)
144
}
145
console.error("onShippingContactSelected error.", exMsg(err));
146
reject({errors: [err]});
147
}
148
})
149
},
150
onShippingMethodSelected: function(shippingMethod) {
151
const self = this;
152
return new Promise(function (resolve, reject) {
153
try {
154
console.log("shippingMethod", JSON.stringify(shippingMethod));
155
const resp = self.getTransactionInfo(null, shippingMethod);
156
resolve(resp);
157
} catch (err) {
158
const apErr = {
159
code: "-102",
160
contactField: "",
161
message: exMsg(err)
162
}
163
console.error("onShippingMethodSelected error.", exMsg(err));
164
reject({errors: [err]});
165
}
166
})
167
},
168
onPaymentMethodSelected: function(paymentMethod) {
169
const self = this;
170
return new Promise((resolve, reject) => {
171
try {
172
console.log("paymentMethod", JSON.stringify(paymentMethod));
173
const resp = self.getTransactionInfo(null, null, paymentMethod.type);
174
resolve(resp);
175
} catch (err) {
176
const apErr = {
177
code: "-102",
178
contactField: "",
179
message: exMsg(err)
180
}
181
console.error("onPaymentMethodSelected error.", exMsg(err));
182
reject({errors: [err]});
183
}
184
})
185
},
186
validateApplePayMerchant: function () {
187
return new Promise((resolve, reject) => {
188
try {
189
var xhr = new XMLHttpRequest();
190
xhr.open("POST", "https://api.cardknox.com/applepay/validate");
191
xhr.onload = function () {
192
if (this.status >= 200 && this.status < 300) {
193
console.log("validateApplePayMerchant", JSON.stringify(xhr.response));
194
resolve(xhr.response);
195
} else {
196
console.error("validateApplePayMerchant", JSON.stringify(xhr.response), this.status);
197
reject({
198
status: this.status,
199
statusText: xhr.response
200
});
201
}
202
};
203
xhr.onerror = function () {
204
console.error("validateApplePayMerchant", xhr.statusText, this.status);
205
reject({
206
status: this.status,
207
statusText: xhr.statusText
208
});
209
};
210
xhr.setRequestHeader("Content-Type", "application/json");
211
xhr.send();
212
} catch (err) {
213
setTimeout(function () { console.log("getApplePaySession error: " + exMsg(err)) }, 100);
214
}
215
});
216
},
217
onValidateMerchant: function() {
218
return new Promise((resolve, reject) => {
219
try {
220
this.validateApplePayMerchant()
221
.then((response) => {
222
try {
223
console.log(response);
224
resolve(response);
225
} catch (err) {
226
console.error("validateApplePayMerchant exception.", JSON.stringify(err));
227
reject(err);
228
}
229
})
230
.catch((err) => {
231
console.error("validateApplePayMerchant error.", JSON.stringify(err));
232
reject(err);
233
});
234
} catch (err) {
235
console.error("onValidateMerchant error.", JSON.stringify(err));
236
reject(err);
237
}
238
});
239
},
240
authorize: function(applePayload, totalAmount) {
241
return new Promise(function (resolve, reject) {
242
var xhr = new XMLHttpRequest();
243
xhr.open("POST", "https://<your domain>/<path to handle authorization>");
244
xhr.onload = function () {
245
if (this.status >= 200 && this.status < 300) {
246
resolve(xhr.response);
247
} else {
248
reject({
249
status: this.status,
250
statusText: xhr.statusText
251
});
252
}
253
};
254
xhr.onerror = function () {
255
reject({
256
status: this.status,
257
statusText: xhr.statusText
258
});
259
};
260
const data = {
261
amount: totalAmount,
262
payload: applePayload
263
};
264
xhr.setRequestHeader("Content-Type", "application/json");
265
xhr.send(JSON.stringify(data));
266
});
267
},
268
onPaymentAuthorize: function(applePayload) {
269
return new Promise((resolve, reject) => {
270
try {
271
this.authorize(applePayload, this.totalAmount)
272
.then((response) => {
273
try {
274
console.log(response);
275
const resp = JSON.parse(response);
276
if (!resp)
277
throw "Invalid response: "+ response;
278
if (resp.xError) {
279
throw resp;
280
}
281
resolve(response);
282
} catch (err) {
283
throw err;
284
// reject(err);
285
}
286
})
287
.catch((err) => {
288
console.error("authorizeAPay error.", JSON.stringify(err));
289
apRequest.handleAPError(err);
290
reject(err);
291
});
292
} catch (err) {
293
console.error("onPaymentAuthorize error.", JSON.stringify(err));
294
apRequest.handleAPError(err);
295
reject(err);
296
}
297
});
298
},
299
onPaymentComplete: function(paymentComplete) {
300
if (paymentComplete.response) { //Success
301
const resp = JSON.parse(paymentComplete.response);
302
if (resp.xRefNum) {
303
setAPPayload("Thank you for your order:("+resp.xRefNum+")");
304
} else {
305
setAPPayload("Thank you for your order.");
306
}
307
} else if (paymentComplete.error) {
308
console.error("onPaymentComplete", exMsg(paymentComplete.error));
309
handleAPError(paymentComplete.error);
310
}
311
},
312
handleAPError: function(err) {
313
if (err && err.xRefNum) {
314
setAPPayload("There was a problem with your order:("+err.xRefNum+")");
315
} else {
316
setAPPayload("There was a problem with your order:"+exMsg(err));
317
}
318
},
319
initAP: function() {
320
return {
321
buttonOptions: this.buttonOptions,
322
merchantIdentifier: "<Your Apple Merchant ID>",
323
requiredBillingContactFields: ['postalAddress', 'name', 'phone', 'email'],
324
requiredShippingContactFields: ['postalAddress', 'name', 'phone', 'email'],
325
onGetTransactionInfo: "apRequest.onGetTransactionInfo",
326
onGetShippingMethods: "apRequest.onGetShippingMethods",
327
onShippingContactSelected: "apRequest.onShippingContactSelected",
328
onShippingMethodSelected: "apRequest.onShippingMethodSelected",
329
onPaymentMethodSelected: "apRequest.onPaymentMethodSelected",
330
onValidateMerchant: "apRequest.onValidateMerchant",
331
onPaymentAuthorize: "apRequest.onPaymentAuthorize",
332
onPaymentComplete: "apRequest.onPaymentComplete",
333
onAPButtonLoaded: "apRequest.apButtonLoaded",
334
isDebug: true
335
};
336
},
337
apButtonLoaded: function(resp) {
338
if (!resp) return;
339
if (resp.status === iStatus.success) {
340
showHide(this.buttonOptions.buttonContainer, true);
341
showHide("lbAPPayload", true);
342
} else if (resp.reason) {
343
console.log(resp.reason);
344
}
345
}
346
};
347
348
function setAPPayload(value) {
349
const apTxt = document.getElementById('ap-payload');
350
apTxt.value = value;
351
showHide(apTxt, value);
352
}
353
​
354
function showHide(elem, toShow) {
355
if (typeof(elem) === "string") {
356
elem = document.getElementById(elem);
357
}
358
if (elem) {
359
toShow ? elem.classList.remove("hidden") : elem.classList.add("hidden");
360
}
361
}
362
function getAmount () {
363
return roundToNumber(document.getElementById("amount").value || "0", 2);
364
}
365
</script>
366
<style>
367
body {
368
margin: 10px;
369
}
370
div.main {
371
width: 350px;
372
}
373
.ap {
374
border: 0;
375
width: 250px;
376
height: auto;
377
min-height: 55px;
378
padding: 0px;
379
margin-bottom: 12px;
380
}
381
​
382
input {
383
border: 1px solid black;
384
font-size: 14px;
385
padding: 3px;
386
width: 250px;
387
margin-bottom: 12px;
388
}
389
​
390
.hidden {
391
display: none;
392
}
393
​
394
textarea {
395
border: 1px solid black;
396
width: 100%;
397
}
398
</style>
399
</head>
400
<body>
401
<div class="main">
402
<form id="payment-form" method="POST">
403
<input id="amount" name="xAmount" placeholder="Amount" type="number" inputmode="decimal"></input>
404
<br/>
405
<div id="ap-container" class="ap hidden" >
406
<br/>
407
</div>
408
<br/>
409
<label id="lbAPPayload" class="hidden">Apple Pay Payload: </label>
410
<br />
411
<textarea id="ap-payload" class="hidden" rows="10" readonly="true"></textarea>
412
<br/>
413
</form>
414
</div>
415
</body>
416
</html>
Copied!
Export as PDF
Copy link