进行付款
本主题介绍使用您通过付款页上的托管字段从付款人处收集敏感付款数据的会话进行付款所需采取的步骤。 这使用 Session JavaScript library (session.js) 以及 WSAPI 完成。 有关网站上整个付款页实现的完整代码段的示例,请参阅付款页代码示例。
步骤 1: 创建会话
通过从您的服务器提交 Create Session 请求来创建会话。 指定会话身份验证限制为 25。响应将返回一个会话 ID,您必须在后续步骤中使用该会话 ID 来引用此会话。
| URL | https://test-anb.mtf.gateway.mastercard.com/api/rest/version/72/merchant/<merchant_ID>/session |
| HTTP 方法 | POST |
{
"session": {
"authenticationLimit": 25
}
}
步骤 2: 使用订单详细信息更新会话
通过从您的服务器提交 Update Session 请求来更新会话,至少更新货币和订单金额。 需要订单货币,以便您可以确定付款人想要使用的信用卡是否受支持,以及是否需要提供卡安全码 (CSC)。
| URL | https://test-anb.mtf.gateway.mastercard.com/api/rest/version/72/merchant/<merchant_ID>/session/<session_ID> |
| HTTP 方法 | PUT |
{
"order": {
"amount": 100,
"currency": "USD"
}
}
步骤 3: 包含 Session JavaScript 库
通过在 head 元素内添加 script 元素,在付款页包含网关托管的 session.js 客户端 JavaScript 库。 此文件的路径包括 API 版本和会话的商家识别码。 这会将 PaymentSession 对象放入窗口名称空间。
<html> <head> <script src="https://test-anb.mtf.gateway.mastercard.com/form/version/72/merchant/<MERCHANTID>/session.js"></script> </head> </html>
步骤 4: 创建付款页
为您的付款页创建 HTML 代码,包括从付款人处收集必要的付款详细信息的字段。
readonly,并去掉 name 属性。您可以使用以下一种或多种付款方式从付款人处捕获付款详细信息。 您需要在付款页上包含的字段取决于付款方式:
信用卡和借记卡
您可以在托管字段中捕获以下卡详细信息:
card.numbercard.expiryMonthcard.expiryYearcard.securityCodecard.nameOnCard
card.expiryMonth,card.expiryYear 则是强制性的,反之亦然。Automated Clearing House (ACH) 付款
您可以通过Automated Clearing House 捕获直接付款(付款)和直接存款(退款)的付款详细信息。 您可以在托管字段中捕获以下 Automated Clearing House 详细信息:
ach.routingNumberach.bankAccountNumberach.bankAccountNumberConfirmationach.bankAccountHolderach.accountType
欲了解更多信息,请参阅 Automated Clearing House。
Click to Pay
您可以从 Click to Pay 交互捕获付款详细信息。 有关更多信息,请参阅 Click to Pay Hosted Session 集成。
以下示例代码说明了信用卡付款所需的付款页字段。
<!-- CREATE THE HTML FOR THE PAYMENT PAGE -->
<div>
Please enter your payment details:
</div>
<div>
Cardholder Name:
<input type="text" id="cardholder-name" class="input-field" title="cardholder name"
aria-label="enter name on card" value="" tabindex="1" readonly>
</div>
<div>
Card Number:
<input type="text" id="card-number" class="input-field" title="card number"
aria-label="enter your card number" value="" tabindex="2" readonly>
</div>
<div>
Expiry Month:
<input type="text" id="expiry-month" class="input-field" title="expiry month"
aria-label="two digit expiry month" value="" tabindex="3" readonly>
</div>
<div>
Expiry Year:
<input type="text" id="expiry-year" class="input-field" title="expiry year"
aria-label="two digit expiry year" value="" tabindex="4" readonly>
</div>
<div>
Security Code:
<input type="text" id="security-code" class="input-field" title="security code"
aria-label="three digit CCV security code" value="" tabindex="5" readonly>
</div>
<div>
<button id="payButton" onclick="pay();">
Pay Now
</button>
</div>
步骤 5: 配置会话
调用 PaymentSession.configure() 函数,以配置对象作为参数,将托管字段附加到您的付款页并配置付款交互。 您需要在配置对象中提供以下内容:
- 创建会话时收到的会话 ID。
- 特定付款方式的托管字段的字段选择器。 配置会将它们替换为嵌入 Mastercard Gateway 托管的内嵌框架的相应代理字段。 代理字段具有与替换字段相同的外观。
- 阻止点阅绑架的缓解选项。 点阅绑架又称“UI 纠正攻击”:用户打算点击最上层页面时,攻击者使用多个透明或不透明层欺骗用户点击另一页面上的按钮或链接。 要使用 Hosted Session,您必须实施以下一种或多种针对点阅绑架攻击的防御措施,并使用 frameEmbeddingMitigation 字段指定实施了哪些防御措施:
- Javascript: 在您的付款页上包含“frame-breaker”JavaScript。
- x-frame-options: 您的服务器将返回 X 框架选项 HTTP 响应标头。
- csp: 您的服务器返回一个包含 frame-ancestors 指令的 Content-Security-Policy HTTP 响应标头。
有关防范点阅绑架攻击的信息,请参阅 OWASP 外部网站的点阅绑架防范备忘单。
- Hosted Session 交互期间处理各个事件的回调:
- 当托管字段附加到您的付款页时,将调用 initialized()。
- formSessionUpdate() 在响应 PaymentSession.updateSessionFromForm(paymentType) 函数时调用(参见下一步)。
- 交互详细信息定义某些显示信息的可见性和付款人交互选项。
PaymentSession.configure({
session: “<your_session_ID>”,
fields: {
// ATTACH HOSTED FIELDS TO YOUR PAYMENT PAGE FOR A CREDIT CARD
card: {
number: “#card-number”,
securityCode: “#security-code”,
expiryMonth: “#expiry-month”,
expiryYear: “#expiry-year”,
nameOnCard: “#cardholder-name”
}
},
//SPECIFY YOUR MITIGATION OPTION HERE
frameEmbeddingMitigation: [“javascript”],
callbacks: {
initialized: function(response) {
// HANDLE INITIALIZATION RESPONSE
},
formSessionUpdate: function(response) {
// HANDLE RESPONSE FOR UPDATE SESSION
},
},
interaction: {
displayControl: {
formatCard: “EMBOSSED”,
invalidFieldCharacters: “REJECT”
}
}
});
步骤 6: 在会话中更新字段详细信息
付款人在托管字段中输入付款详细信息后,系统将使用适用的付款方式作为参数调用 PaymentSession.updateSessionFromForm() 函数。 该函数将捕获的付款详细信息存储到付款会话中。 一旦操作完成,就会使用结果参数调用 formSessionUpdate() 回调。 检查 result.status 字段以确定操作是否成功。 有关更多信息,请参阅处理回调响应。
function pay() {
// UPDATE THE SESSION WITH THE INPUT FROM HOSTED FIELDS
PaymentSession.updateSessionFromForm('card');
}
步骤 7: 使用会话创建付款
使用请求中的会话 ID (session.id) 将付款交易(或其他相关操作)从您的服务器发送到网关:
- 发送 Retrieve Session 请求验证会话中包含的详细信息。
- 通过添加会话中未包含的任何必要字段来创建交易请求。
- 发送交易请求。
您可以使用同一个会话发送多个与付款相关的操作。 例如,您既可以通过 PAY 操作发起付款,也可以通过 Create or Update Token 操作存储代表付款详细信息的令牌(供将来的交易使用)。
付款页代码示例
以下示例代码显示完整付款页的 HTML 代码。
<html>
<head>
// INCLUDE SESSION.JS JAVASCRIPT LIBRARY
<script src="https://test-anb.mtf.gateway.mastercard.com/form/version/<version>/merchant/<merchant_ID>/session.js"></script>
// APPLY CLICK-JACKING STYLING AND HIDE CONTENTS OF THE PAGE
<style id="antiClickjack">body{display:none !important;}>/style>
</head>
<body>
// CREATE THE HTML FOR THE PAYMENT PAGE
<div>Please enter your payment details:</div>
<h3>Credit Card</h3>
<div>Card Number: <input type="text" id="card-number" class="input-field" title="card number" aria-label="enter your card number" value="" tabindex="1" readonly></div>
<div>Expiry Month:<input type="text" id="expiry-month" class="input-field" title="expiry month" aria-label="two digit expiry month" value="" tabindex="2" readonly></div>
<div>Expiry Year:<input type="text" id="expiry-year" class="input-field" title="expiry year" aria-label="two digit expiry year" value="" tabindex="3" readonly></div>
<div>Security Code:<input type="text" id="security-code" class="input-field" title="security code" aria-label="three digit CCV security code" value="" tabindex="4" readonly></div>
<div>Cardholder Name:<input type="text" id="cardholder-name" class="input-field" title="cardholder name" aria-label="enter name on card" value="" tabindex="5" readonly></div>
<div><button id="payButton" onclick="pay('card');">Pay Now</button></div>
// JAVASCRIPT FRAME-BREAKER CODE TO PROVIDE PROTECTION AGAINST IFRAME CLICK-JACKING
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
PaymentSession.configure({
session: "<your_session_ID>",
fields: {
// ATTACH HOSTED FIELDS TO YOUR PAYMENT PAGE FOR A CREDIT CARD
card: {
number: "#card-number",
securityCode: "#security-code",
expiryMonth: "#expiry-month",
expiryYear: "#expiry-year",
nameOnCard: "#cardholder-name"
}
},
//SPECIFY YOUR MITIGATION OPTION HERE
frameEmbeddingMitigation: ["javascript"],
callbacks: {
initialized: function(response) {
// HANDLE INITIALIZATION RESPONSE
},
formSessionUpdate: function(response) {
// HANDLE RESPONSE FOR UPDATE SESSION
if (response.status) {
if ("ok" == response.status) {
console.log("Session updated with data: " + response.session.id);
//check if the security code was provided by the user
if (response.sourceOfFunds.provided.card.securityCode) {
console.log("Security code was provided.");
}
//check if the user entered a Mastercard credit card
if (response.sourceOfFunds.provided.card.scheme == 'MASTERCARD') {
console.log("The user entered a Mastercard credit card.")
}
} else if ("fields_in_error" == response.status) {
console.log("Session update failed with field errors.");
if (response.errors.cardNumber) {
console.log("Card number invalid or missing.");
}
if (response.errors.expiryYear) {
console.log("Expiry year invalid or missing.");
}
if (response.errors.expiryMonth) {
console.log("Expiry month invalid or missing.");
}
if (response.errors.securityCode) {
console.log("Security code invalid.");
}
} else if ("request_timeout" == response.status) {
console.log("Session update failed with request timeout: " + response.errors.message);
} else if ("system_error" == response.status) {
console.log("Session update failed with system error: " + response.errors.message);
}
} else {
console.log("Session update failed: " + response);
}
}
},
interaction: {
displayControl: {
formatCard: "EMBOSSED",
invalidFieldCharacters: "REJECT"
}
}
});
function pay() {
// UPDATE THE SESSION WITH THE INPUT FROM HOSTED FIELDS
PaymentSession.updateSessionFromForm('card');
}
</script>
</body>
</html>
付款页回调
Hosted Session 允许您使用各种回调来自定义付款页的行为方式以及向付款人提供哪一类反馈。
会话配置的回调
此部分定义会话配置回调及其结果回调返回的响应。 有关如何处理付款页代码中的回调的示例,请参阅付款页代码示例。
PaymentSession.configure() 函数中使用的回调:
- 当托管字段附加到付款页时,将调用 initialized(result) 回调:
- 如果 result.status=="ok",则托管字段已成功附加到您的付款页。
- 如果 result.status=="system_error" 或 result.status=="request_timeout",则表示附加托管字段时发生了错误。 在短暂延迟后重试。
成功初始化响应示例// An ok response { "status":"ok", }失败初始化响应示例// An error response (system_error) { "status": "system_error", "message": "System error message." } // An error response (request_timeout) { "status" : "request_timeout", "message": "Request timeout error message." } - 当托管字段内容存储在会话中时,调用 formSessionUpdate(result) 回调:
- 如果 result.status=="ok",说明会话现在包含收集的付款详细信息。
成功响应的表单会话更新示例// An ok response { "status":"ok", "merchant": "TESTMERCHANT", "session": { "id": "SESSION000218450948092491657986" "updateStatus":"SUCCESS", "version":"e3f144ce02" }, "sourceOfFunds": { "provided": { "card": { "brand": "MASTERCARD", "expiry": { "month": "1", "year": "39" }, "fundingMethod": "DEBIT", "nameOnCard": "John Smith", "number": "512345xxxxxx8769", "scheme": "MASTERCARD" } }, "type": "CARD" }, "version": "43" } - 如果 result.status=="fields_in_error",则付款人输入无效。 提示付款人更新其输入。 errors 响应结构包含有关无效字段的信息。
- 如果 result.status=="system_error" 或 result.status=="request_timeout",则表示处理更新时发生了错误。 在短暂延迟后重试会话更新。
// An error response (fields_in_error)
{
"status": "fields_in_error",
"session": {
"id": "SESSION000218450948092491657986"
},
"errors": {
"cardNumber": "invalid",
"securityCode": "invalid"
},
version: "43"
}
// An error response (system_error)
{
"status": "system_error",
"session": {
"id": "SESSION000218450948092491657986"
},
"errors": {
"message": "System error message."
},
"version": "43"
}
// An error response (request_timeout)
{
"status": "request_timeout",
"session": {
"id": "SESSION000218450948092491657986"
},
"errors": {
"message": "Request timeout error message."
},
"version": "43"
}
托管字段的回调
Hosted Session 允许您注册回调函数来处理付款人交互期间托管字段中可能会发生的事件。 通过这些事件,您可以跟踪付款人的行为,并在各个付款交互阶段向他们提供验证反馈。
您可以为以下事件注册回调函数:
- onChange(): 在内嵌框架托管字段内的输入值更改时调用。
- onFocus(): 在内嵌框架中的托管字段获得焦点时调用。
- onBlur(): 在内嵌框架中的托管字段失去焦点时调用。 付款人完成键入并离开字段,并且此事件被触发后,即调用 validate() 函数并从 validate() 函数的回调显示字段的所有错误。
- onMouseOver(): 当鼠标置于托管字段中发生的事件时调用。
- onMouseOut(): 当鼠标从托管字段中发生的事件上移开时调用。
- onValidityChange(): 在付款人每次击键后调用,提供有关付款人迄今为止数据输入的有效性的反馈。
/**
* Provide an array of field roles for proxy fields as the first parameter
* Each callback function is invoked with the selector for the field whose proxy triggered the event.
*/
PaymentSession.onBlur( ["card.number", "card.nameOnCard", "card.securityCode", "card.expiryYear", "card.expiryMonth"],
function (selector, role)
{ PaymentSession.validate('card', function (allresult) {
if (allresult.card[role].isValid) {
console.log("The field is valid");
document.querySelector(selector).style.borderColor = "green";
} else {
console.log("The field is invalid");
document.querySelector(selector).style.borderColor = "red";
}
});
PaymentSession.onFocus(['card.number', 'card.securityCode'], function(selector) {
//handle focus event
});
PaymentSession.onChange(['card.securityCode'], function(selector) {
//handle change event
});
PaymentSession.onMouseOver(['card.number'], function(selector) {
//handle mouse over event
});
PaymentSession.onMouseOut(['card.number'], function(selector) {
//handle mouse out event
});
PaymentSession.onValidityChange(["card.number", "card.nameOnCard"], function (selector, result) {
if (result.isValid) {
console.log("The field value is valid");
document.querySelector(selector).style.borderColor = "green";
} else if (result.isIncomplete) {
console.log("The field value is not yet valid");
document.querySelector(selector).style.borderColor = "grey";
} else {
console.log("The field value is invalid");
document.querySelector(selector).style.borderColor = "red";
}
});
常见问题
如果我的商家配置文件不支持付款人输入的卡类型我该如何处理?
要处理此事件,首先使用 PAYMENT OPTIONS INQUIRY 操作来获取支持的卡类型列表。 然后检查 PaymentSession.updateSessionFromForm('card') 响应中的卡类型信息(sourceOfFunds.provided.card.brand 和 sourceOfFunds.provided.card.scheme),对照支持的卡类型列表进行验证,如果不接受该卡类型,显示错误消息。
我如何知道付款人的 CSC 或 CVV 是否需要且已提供?
要了解是否需要 CSC 或 CVV,使用 PAYMENT OPTIONS INQUIRY 操作。 如果付款人未提供 CSC/CVV,PaymentSession.updateSessionFromForm('card') 响应中不会返回 securityCode 字段。 如果您需要 CSC/CVV 但没有任何值,则需要向付款人显示错误。
是否所有浏览器都可以执行托管字段的事件回调?
以下操作系统和浏览器存在已知的事件回调问题:
- Windows 10 中的 Internet Explorer 11: 如果 interaction.displayControl.formatCard=EMBOSSED,则更改托管字段的值时不会触发 onChange() 事件。
- iPhone 6+ 中的 iOS9: 当付款人在托管字段中输入数据并点击付款页中的另一个字段时,不会触发 onChange() 和 onBlur() 事件。 此外,付款人还无法从托管字段导航至付款页的其他字段,反之亦然。