Apple Pay Hosted Checkout Sample Code

  1. Let's define a helper object with all necessary components:

    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) {
                    setTimeout(function(){ console.log("Thank you for your order:("+resp.xRefNum+")")}, 100);
                } else {
                    setTimeout(function(){ console.log("Thank you for your order.")}, 100);
                }
            } else if (paymentComplete.error) {
                console.error("onPaymentComplete", exMsg(paymentComplete.error));
                handleAPError(paymentComplete.error);
            }                        
        },
        handleAPError: function(err) {
            if (err && err.xRefNum) {
                setTimeout(function(){ alert("There was a problem with your order:("+err.xRefNum+")")}, 100);
            } else {
                setTimeout(function(){ alert("There was a problem with your order:"+exMsg(err))}, 100);
            }
        },
        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);
            }
        }
    }
  2. Let’s enable Apple Pay for the website:

    document.addEventListener("DOMContentLoaded", function(event) { 
        .....
        ckApplePay.enableApplePay({
            initFunction: 'apRequest.initAP',
            amountField: 'amount'
        });
        .....
    }
  3. To see the full solution please click here.

Last updated