Apple Pay on Web 串接

Apple_Pay_Mark_RGB_041619
串接概要
  1. ApplePay 網域憑證驗證
  2. 載入SDK
  3. 設置 SDK
  4. 設置 Apple Pay
  5. 設置 PaymentRequest
  6. 取得 Prime 並自定義 callback function 使用 Prime
  7. Get Prime Result
  8. 使用 Prime 進行交易授權請求
事前準備

*ACpay 提供之 SDK 預設依照 Apple 之規範產生按鈕

1. ApplePay 網域憑證驗證

將 ACpay 提供之 apple-developer-merchantid-domain-association.txt 放至 https://<特店網域>/.well-known/apple-developer-merchantid-domain-association.txt,待 ACpay 驗證並告知完成即可。

­­*若使用 ACpay 測試專用特店可略過,但正式區須重新申請。
2. 載入SDK
載入apple 提供api 來源及 ACpay 最新SDK (2023/05 update v1.0.3)
				
					<script src="https://connectjs.payloop.com.tw/ACconnect/v1.0.3.js" ></script>
				
			
3. 設置SDK

­­將申請之相關參數設置完成以使用SDK。

				
					ACconnect.setupSDK('appID', 'appKey', 'serverType');
				
			

名稱(*=必填)

類別

內容

appID*

int

ACpay 提供之驗證識別碼

appKey*

String

ACpay 提供之驗證金鑰

serverType*

String

使用的伺服器種類:
– 測試時請使用 Sandbox 環境(sandbox)
– 上線後請切換至 Production 環境 (production)

4. 設置 Apple Pay

初始化ApplePay API所需相關參數。

				
					
ACconnect.ApplePay.setupApplePay({
    merchantIdentifier: 'merchantIdentifier provided by ACpay'
});
				
			

名稱 (*=必填)

類別

內容

merchantIdentifier*

String

ACpay 提供之 Apple Developer 核可的 Merchant Identifier

 

5. 設置 PaymentRequest

建立一個 paymentRequest 的物件設定交易資訊來索取 Prime。 LineItems 內容為商品明細資訊僅供消費者顯示作用,其計算之金額與實際授權無關,須由串接方同步至後續發動授權之電文金額,最後將以授權之金額為主。

				
					var paymentRequest = {
    "totalLabel": "Merchant Name Show Customer",
    "lineItems": [
        {
            "label": "items1",
            "amount": "1.00"
        },
        {
            "label": "items2",
            "amount": "5.00"
        }
    ]
};
				
			
名稱類別內容
lineItems (必填)Array
名稱類別內容
labelString明細名稱
amountString金額(台幣)
totalLabel(必填)String商戶名稱
shippingMethodsArray

運送方式

名稱類別內容
identifierString運送名稱
labelString運送方式介紹
detailString運送方式詳細說明 
amountString金額(台幣)
dateComponentsRangeJSONObject

運送出貨、到貨時間

包含:

startDateComponents、

endDateComponents

兩者內容:

years:年

months:月

days:日

hours:小時(凌晨為0)

(此項目只支援IOS15以上之裝置)

requiredShippingContactFieldsArray

設定需要消費者的那些運輸寄送資訊,目前可帶入 

“email”、”name”、”phone”、”postalAddress”、”phoneticName”

requiredBillingContactFieldsArray

設定需要消費者的那些帳單寄送資訊,目前可帶入 

“email”、”name”、”phone”、”postalAddress”、”phoneticName”

shippingContactJSONObject

店家預設收件地址。

名稱類別內容
addressLinesString地址
localityString
administrativeAreaString縣/市 
postalCodeString郵遞區號
countryCodeString國碼
familyNameString姓氏/公司名稱
shippingContactEditingModeString

是否允許使用者編輯運送地點

(此項目只支援IOS15以上之裝置)

“enabled” = 允許,

“storePickup” = 不允許

shippingTypeString

運送方式,目前可帶入 shipping、delivery、storePickup、servicePickup

預設為shipping。

設定好 payment request 物件後, 透過以下方法設定 Payment Request API。

				
					/**
    使用ACconnect.ApplePay.getPrime 向 ACconnect 取得付款用的 prime
*/
ACconnect.ApplePay.getPrime(function(result) {
    console.log('paymentRequestApi.getPrime result', result);
    if (result.status !== 0) {
        console.error('getPrime failed: ' + result.msg);
        return
    }
    var prime = result.prime;
});
				
			
				
					/**
    使用 ACconnect.ApplePay.setupPaymentRequest 設定 payment request 物件
    並在 callback function 回傳物件關於使用者的瀏覽器是否有卡片進行付款
*/
Acconnect.ApplePay.setupPaymentRequest(paymentRequest, function (result) {
if (!result.browserSupportApplePaySession) {
        return;
    }
    if (result.canMakePaymentWithActiveCard === true) {
        console.log('該裝置有支援的卡片可以付款');
    } else {
        console.log('該裝置沒有支援的卡片可以付款');
    }
});

				
			

setupPaymentRequest 的 callback 函式, 會回傳以下物件屬性。

名稱

類別

內容

result

JSONObject

名稱

類別

內容

canMakePaymentWithActiveCard

Boolean

檢查使用者是否有符合JCB、VISA、masterCard


true: 有卡片
false: 無卡片

browserSupportApplePaySession

Boolean

檢查消費者瀏覽器能否使用ApplePaySession

true: 可以使用
false: 不可使用

6. 取得 Prime 並自定義 callback function 使用 Prime
依照店家使用需求取得 api 回傳 Prime 並進行後續交易授權所需處理。
				
					/**
    使用ACconnect.ApplePay.getPrime 向 ACconnect 取得付款用的 prime
*/
ACconnect.ApplePay.getPrime(function(result) {
    console.log('paymentRequestApi.getPrime result', result);
    if (result.status !== 0) {
        console.error('getPrime failed: ' + result.msg);
        return
    }
    var prime = result.prime;
});
				
			
7. Get Prime Result

getPrime 成功會取得 Result 物件之資訊,內容參數說明如下。

				
					{
    "prime": String,
    // 加上運費的價格
    "total_amount": String,
    "status": Int,
    "msg": String,
    "client_ip": String,
    "shippingContact": {
	"addressLines": ArrayString,
	"administrativeArea": String,
        "country": String,
	"countryCode": String,
        "emailAddress": String,
        "familyName": String,
        "givenName": String,
        "locality": String,
        "phoneNumber": String,
	"phoneticFamilyName": String,
	"phoneticGivenName": String,
	"postalCode": String,
	"subAdministrativeArea": String,
	"subLocality": String
    },
    "billingContact": {
        "addressLines": ArrayString,
	"administrativeArea": String,
        "country": String,
	"countryCode": String,
        "familyName": String,
        "givenName": String,
        "locality": String,
	"phoneticFamilyName": String,
	"phoneticGivenName": String,
	"postalCode": String,
	"subAdministrativeArea": String,
	"subLocality": String
    },
    "card_info": {
        "bincode": String,
        "lastfour": String,
        "issuer": String,
        "issuer_zh_tw": String,
        "bank_id": String,
        "funding": String,
        "type": String,
        "level": String,
        "country": String,
        "countrycode": String,
    },
    "expiryMillis": Number
}

				
			

名稱

類別

內容

prime

String

prime 字串

total_amount

String

總金額(台幣)

status

Int

交易代碼,成功的話為 0

msg

String

錯誤訊息

client_ip

String

消費者的 IP 位置

shippingContact

JSONObject

運送聯絡資料

名稱

類別

內容

   

addressLines

ArrayString

地址

administrativeArea

String

縣市

   

country

String

國家

   

countryCode

String

國碼(two-letter ISO 3166 country code)

emailAddress

String

收件人電子郵件

familyName

String

姓氏

givenName

String

收件人名稱

locality

String

phoneNumber

String

收件人電話號碼

phoneticFamilyName

String

收件人姓氏音節

phoneticGivenName

String

收件人名稱音節

postalCode

String

郵遞區號

billingContact

JSONObject

帳單聯絡資料

名稱

類別

內容

   

addressLines

ArrayString

地址欄

administrativeArea

String

縣市

   

country

String

國家

   

countryCode

String

國碼(two-letter ISO 3166 country code)

familyName

String

姓氏

givenName

String

收件人名稱

locality

String

phoneticFamilyName

String

收件人姓氏音節

phoneticGivenName

String

收件人名稱音節

postalCode

String

郵遞區號

card_info

JSONObject

卡片資訊,將會回傳以下幾個值:

名稱

類別(長度)

內容

bincode

String(6)

卡片前六碼

lastfour

String(4)

卡片後四碼

issuer

String

發卡銀行

issuer_zh_tw

String

發卡銀行中文名稱

bank_id

String

發卡銀行代碼

funding

int

卡片類別
-1 = Unknown
0 = 信用卡 (Credit Card)
1 = 簽帳卡 (Debit Card)
2 = 預付卡 (Prepaid Card)

type

int

卡片種類
-1 = Unknown
1 = VISA
2 = MasterCard
3 = JCB
4 = Union Pay
5 = AMEX

level

String

卡片等級

country

String

發卡行國家

countrycode

String

發卡行國家碼

expiryMillis

Number

Prime 到期時間

8. 使用 Prime 進行交易授權請求
將 Result 內取得之 Prime 及當前交易資訊組成授權電文對 ACpay 進行交易授權請求。(詳細請見網路特店暨線上授權服務系統 付款服務使用手冊)。

*請注意交易將以電文授權之金額為主,與前述步驟之 LineItems 計算之金額需相同避免顯示於消費者與實際授權金額不同。
Example Code:
				
					<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- <link rel="stylesheet" type="text/css" href="./style.css"> -->
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.css">
    
    <script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script src="https://connectjs.payloop.com.tw/ACconnect/v1.0.3.js"></script>

    <title>Test Apple Pay</title>
    <style>
        .ui.meesage {
            transition: all .5s ease
        }
        .apple-pay-button {
            -webkit-appearance: -apple-pay-button;
            width: 200px;
            min-height: 30px;
            border: 1px solid black;
            background-image: -webkit-named-image(apple-pay-logo-black);
            background-size: 100% calc(60% + 2px);
            background-repeat: no-repeat;
            background-color: white;
            background-position: 50% 50%;
            border-radius: 5px;
            padding: 10px;
            margin: 5px auto;
            transition: background-color .15s;
        }
        .apple-pay-button.buy {
            /* plain, buy, set-up, donate */
            -apple-pay-button-type: buy; 
        }
        .apple-pay-button.set-up {
            -apple-pay-button-type: set-up; 
        }
    </style>
</head>

<body>
    <div class="ui grid centered stackable">
        <div class="fourteen wide column">
            <form class="ui form">

                <!-- apple pay -->
                <div id="apple-pay" class="apple-pay-button" style="display: none;"></div>
                <!-- payment request -->
                <button id="pay" type="button" class="ui medium button" style="display: none;">Pay By Prime</button>
            
            </form>

            <div class="ui info message support">
                <div class="ui header">裝置支援:</div>
                <pre class="content" id="support">檢查中...</pre>
            </div>
            
            <div class="ui info message hidden result1">
                <div class="ui header">Get Prime Result</div>
                <pre class="content" id="result1" style="overflow-x: auto;"></pre>
            </div>
            <div class="ui info message hidden curl">
                <div class="ui header">Use following data to send to server</div>
                <pre class="content" id="curl" style="overflow-x: auto;"></pre>
            </div>
        </div>
    </div>

<script>    
    document.addEventListener('DOMContentLoaded', () => {
        var pay_button
        if (window.ApplePaySession) {
            var paymentRequest = {
                    "totalLabel": "交流飲料店",
                    "lineItems":[
                        {
                            "label":"珍珠奶茶",
                            "amount":"20.00"
                        },
                        {
                            "label":"檸檬紅茶",
                            "amount":"15.00"
                        }

                    ]
                    
            };

            ACconnect.setupSDK('090453990201003', 'e509c975-679e-4040-9d82-f2e2de6227b1', 'sandbox') // (appId, appKey, environment)
            ACconnect.ApplePay.setupApplePay({
                // required, your apple merchant id
                merchantIdentifier: 'merchant.test090453990201003'
            })
            ACconnect.ApplePay.setupPaymentRequest(paymentRequest, function (result) {
                console.log('paymentRequestApi.getPrime result', result)

                // 代表瀏覽器支援 ApplePaySession (或 apple pay)
                if (!result.browserSupportApplePaySession) {
                    return
                }

                // 代表使用者是否有符合 JCB、VISA、MasterCard 的卡片
                // apple pay         ---> canMakePaymentWithActiveCard is result of canMakePaymentsWithActiveCard

                if (result.canMakePaymentWithActiveCard) {
                    document.getElementById('support').textContent = '裝置可以使用 PaymentRequest / Apple Pay'
                    $('#apple-pay').addClass('buy')
                }
                else {
                    // apple pay,會引導使用者去 apple pay 綁卡片
                    document.getElementById('support').textContent = '裝置支援 PaymentRequest / Apple Pay,但是沒有可以支付的卡片'
                    $('#apple-pay').addClass('set-up')
                }

                if (window.ApplePaySession) {
                    pay_button = document.getElementById('apple-pay')
                    pay_button.style.display = 'inline-block';
                }

                pay_button.addEventListener('click', function (event) {
                    ACconnect.ApplePay.getPrime(function(result) {
                        console.log('ACconnect.ApplePay.getPrime result', result)
                        document.querySelector('.result1').classList.remove('hidden')
                        document.querySelector('#result1').innerHTML = JSON.stringify(result, null, 4)

                        if (result.status == 0) {
                            handlePayByPrime(result, result);
                            //關閉使用者裝置的sheet (交易成功)
                            ACconnect.ApplePay.completePayment(0);
                        } else {
                            //關閉使用者裝置的sheet (交易失敗)
                            ACconnect.ApplePay.completePayment(-1);
                        }

                    })
                })
            })
            document.getElementById('support').textContent = '裝置支援 PaymentRequest / Apple Pay'
        }
        else {
            $('.support').removeClass("info").addClass("error")
            document.getElementById('support').textContent = '裝置不支援 PaymentRequest / Apple Pay'
        }
    });

    function handlePayByPrime(result, paymentRequest) {
        document.querySelector('.curl').classList.remove('hidden')

        var command = "";
        command += "Use following command to send to server \n"
        command += "curl -k -X POST https://connectjs.payloop.com.tw/credit \\ "
        command += "-H 'content-type: application/xml' \\ \n"
        command += "-d '<xml><prime><![CDATA["+ result.prime +"]]&gt;</prime>\n"
        command += "<charset><![CDATA[UTF-8]]&gt;</charset>\n"
        command += "<merchant_no><![CDATA[MERCHANT_NO]]&gt;</merchant_no>\n"
        command += "<nonce_str><![CDATA[NONCE]]&gt;</nonce_str>\n"
        command += "<out_trade_no><![CDATA[ORDER_NO]]&gt;</out_trade_no>\n"
        command += "<fill_email><![CDATA[xxx@gmail.com]]&gt;</fill_email>\n"
        command += "<service><![CDATA[intp]]&gt;</service>\n"
        command += "<body><![CDATA[order]]&gt;</body>\n"
        command += "<version><![CDATA[2.0]]&gt;</version>\n"
        command += "<sign_type><![CDATA[SHA-256]]&gt;</sign_type>\n"
        command += "<sign><![CDATA[SIGN]]&gt;</sign>\n"
        command += "</xml>' \n".replace(/                /g, '')

        document.querySelector('#curl').innerText = command

    }
</script>
</body>

</html>