Apple Pay On Web 開發者文件
串接概要
- ApplePay 網域憑證驗證
- 載入SDK
- 設置 SDK
- 設置 Apple Pay
- 設置 PaymentRequest
- 取得 Prime 並自定義 callback function 使用 Prime
- Get Prime Result
- 使用 Prime 進行交易授權請求
事前準備
- 請確認符合《Apple建議之相關開發條件》
- 使用 SDK 套件需遵守並接受《Apple Pay API 使用限制政策》中訂定的條款
- Apple Pay 按鈕設計原則請參照《Apple 品牌規範》:
*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 (2024/12 update v1.0.5)
測試區
<script src="https://connectjs.payloop.com.tw/ACconnect/v1.0.5.js" ></script>正式區
<script src="https://connectjs.acpay.com.tw/ACconnect/v1.0.5.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 | 名稱 | 類別 | 內容 |
| label | String | 明細名稱 | ||
| amount | String | 金額(台幣) |
totalLabel(必填)String商戶名稱shippingMethodsArray
運送方式
| 名稱 | 類別 | 內容 |
|---|---|---|
| identifier | String | 運送名稱 |
| label | String | 運送方式介紹 |
| detail | String | 運送方式詳細說明 |
| amount | String | 金額(台幣) |
| dateComponentsRange | JSONObject | 運送出貨、到貨時間包含:startDateComponents、endDateComponents兩者內容:years:年months:月days:日hours:小時(凌晨為0)(此項目只支援IOS15以上之裝置) |
requiredShippingContactFieldsArray
設定需要消費者的那些運輸寄送資訊,目前可帶入
“email”、”name”、”phone”、”postalAddress”、”phoneticName”
requiredBillingContactFieldsArray
設定需要消費者的那些帳單寄送資訊,目前可帶入
“email”、”name”、”phone”、”postalAddress”、”phoneticName”
shippingContactJSONObject
店家預設收件地址。
| 名稱 | 類別 | 內容 |
|---|---|---|
| addressLines | String | 地址 |
| locality | String | 區 |
| administrativeArea | String | 縣/市 |
| postalCode | String | 郵遞區號 |
| countryCode | String | 國碼 |
| familyName | String | 姓氏/公司名稱 |
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 | 檢查消費者瀏覽器能否使用ApplePaySessiontrue: 可以使用 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('090414780201003', '854b72ab-3828-4994-ab6c-16b5145937a8', '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 +"]]></prime>\n"
command += "<charset><![CDATA[UTF-8]]></charset>\n"
command += "<merchant_no><![CDATA[MERCHANT_NO]]></merchant_no>\n"
command += "<nonce_str><![CDATA[NONCE]]></nonce_str>\n"
command += "<out_trade_no><![CDATA[ORDER_NO]]></out_trade_no>\n"
command += "<fill_email><![CDATA[xxx@gmail.com]]></fill_email>\n"
command += "<service><![CDATA[intp]]></service>\n"
command += "<body><![CDATA[order]]></body>\n"
command += "<version><![CDATA[2.0]]></version>\n"
command += "<sign_type><![CDATA[SHA-256]]></sign_type>\n"
command += "<sign><![CDATA[SIGN]]></sign>\n"
command += "</xml>' \n".replace(/ /g, '')
document.querySelector('#curl').innerText = command
}
</script>
</body>
</html>