使用 3DS JavaScript API 的 3DS
本主题概述了使用 3DS JavaScript(JS) 将 3DS 添加到 American Express Payment Gateway 集成所需的所有步骤,包括如何使用身份验证结果处理付款。
要查看使用 3DS JavaScript API 的 3DS 流中使用的 API 请求和响应的示例,请下载 Postman 集合。
有关您可以在集成中利用的一些通用 3DS 功能的详细信息,请参阅常见问题。 完成集成后,请参阅测试 3DS 集成进行测试。
先决条件
要将 3DS 与 3DS JS API 一起使用:
- 实现基本 Hosted Session 集成。
- 实现您要用于创建的会话的任何后续付款操作。
- 在继续会话进行身份验证之前,创建一个单独的会话来收集卡详细信息。
步骤 1: 创建和更新会话
3DS JS 使用基于会话的身份验证。 有关基于会话的身份验证的更多信息,请参阅“身份验证选项”。 作为第一步,您必须创建会话,然后您可以使用希望存储在会话中的请求字段和值更新会话。
 您可以使用 CREATE SESSION 请求来创建会话。 这是服务器端 WS API 请求,是与 JS API 集成的先决条件。 它返回以下字段:
- session.id
 唯一会话标识符,您必须在后续请求中提供此标识符来引用会话内容。
- session.authenticationLimit
 对付款人浏览器可以提交的交易请求数量的限制。 您可以在请求中提供一个值或使用网关的默认值。 默认情况下,网关将此值设置为 5,不过您可以提供的最大值是 25。此限制可以阻止恶意用户将身份验证请求用作潜在的卡攻击,以及通过提交大量有可能可计费的交易对您的网站执行拒绝服务 (DoS) 攻击。您发起的任何身份验证重试都将根据身份验证限制进行检查。
- session.aes256Key
 您可以用来解密通过付款人的浏览器或移动设备传递到您网站的敏感数据的密钥。
- session.version
 会话版本。 您可以使用此字段来实现会话内容的乐观锁。
- session.updateStatus
 上次尝试修改会话的结果的摘要。
创建会话后,您可以使用 UPDATE SESSION 请求在会话中添加或更新字段。 这使您可以将付款和付款人数据添加到会话中,这些数据随后可以作为输入,以确定身份验证操作中与付款人相关的风险。 下表定义了会话中常用的字段。
 表: 会话字段
| 字段 | 必需/可选 | 说明 | 
|---|---|---|
| session.id或 sourceOfFunds.provided.card.*或sourceOfFunds.token | 必需 | 用于付款的卡的详细信息。 您还可以使用网络令牌和设备付款令牌作为付款人身份验证中的资金来源。 有关详细信息,请参见常见问题。 | 
| order.amount | 必需 | 订单总金额。 | 
| order.currency | 必需 | 订单的货币。 | 
| transaction.id | 必需 | 此支付身份验证的唯一识别码。 | 
| order.id | 必需 | 此订单的唯一标识符。 | 
| authentication.channel | 必需 | 发起身份验证请求的通道。 您可以指定以下其中一项: 
 | 
| authentication.redirectResponseUrl | 必需 | 完成付款人身份验证流程后,您想要将付款人重定向到的 URL。 如果您确定不会与付款人发生互动,则无需提供此 URL。 | 
| authentication.purpose | 可选 | 身份验证的目的。 默认情况下,此字段设置为 PAYMENT_TRANSACTION,以指示在处理卡付款时要执行身份验证。 不过,您可以指定其他目的来指示非支付身份验证。 如果您正在建立付款协议但没有同时处理付款,请提供 ADD_CARD 作为身份验证目的。 请参阅如何提交非支付身份验证请求? 必须提供协议详细信息。 | 
| authentication.acceptVersions | 可选 | 您接受用于此付款的 3DS 版本。 如果您未指定版本,则接受 3DS2。 网关将使用 3DS2(如果发卡机构和卡支持)。 如果 3DS2 不可用,则身份验证无法继续。 | 
| order.merchantCategoryCode | 可选 | 商家类别代码。 在您希望覆盖在收单行链接上配置的默认值时提供值。 | 
步骤 2: 初始化 API
引用来自网关服务器的 3DS JS API (threeDS.js)。 这会将 ThreeDS 对象放入窗口/全局命名空间。
 创建会话后,使用 configure() 函数初始化 3DS JS API,并在 threedsConfig 映射对象中使用以下强制参数:
- merchantId
 您在网关上的商家标识符。
- sessionId
 您使用 CREATE SESSION 请求创建的会话 ID。
- containerId
 API 将在其中插入隐藏内嵌框架的 HTML 代码中的 <div> ID。
- callback
 API 初始化后要调用的函数。
- configuration
 支持数据元素的 JSON 值,例如 userLanguage(向用户显示的付款页语言;可选)和 wsVersion(WS API 版本)。
必须在页面加载期间或 DOM 处于就绪状态时调用 configure() 函数。 只能为页面加载调用一次。 调用此方法后,3DS JS 将配置值作为成员变量提供。
<html>
    <head>
    <script src="../../../../../../../static/threeDS/1.3.0/three-ds.min.js"
            data-error="errorCallback"
            data-cancel="cancelCallback">
    </script>
    <script type="text/javascript">
        //The output of this call will return 'false', since the API is not configured yet
        console.log(ThreeDS.isConfigured());
        /**
        Configure method with the configuration{} parameter set and demonstrates the state change of the ThreeDS object before and after the configure method is invoked.
        */
        ThreeDS.configure({
            merchantId: {merchantId},
            sessionId: {sessionId},
            containerId: "3DSUI",
            callback: function () {
                if (ThreeDS.isConfigured())
                    console.log("Done with configure");
            },
            configuration: {
                userLanguage: "en-AU", //Optional parameter
                wsVersion: 81
            }
        });
        //The output of this call will return 'true', since the API is configured
        console.log(ThreeDS.isConfigured());
        //The output of the following code might look like "ThreeDS JS API Version : 1.2.0"
        console.log("ThreeDS JS API Version : " + ThreeDS.version);
    </script>
    </head>
    <body>
        <div id="3DSUI"></div>
    </body>
</html>
		
步骤 3: 发起身份验证
将所有付款人和付款数据全部收集到会话中后,您即可以通过调用 initiateAuthentication() 函数来发起身份验证。 它调用付款人身份验证 API 的 INITIATE AUTHENTICATION 请求,并根据以下信息确定您可以为给定卡使用的付款人身份验证版本:
- 在您的商家配置文件上配置的 3DS 版本
- 卡类型
- 您在请求中指定的首选项
- 该卡已注册的 3DS 版本
- 您或 Your payment service provider (PSP) 配置的 3DS 交易筛选规则
此函数还支持任何后台活动,如执行 3DS2 ACS 调用,用于收集其他付款人数据以支持后续的 authenticatePayer() 函数等目的。
您可以通过调用包含以下强制参数的 initiateAuthentication() 函数来发起身份验证:
- transactionId
 此支付身份验证的唯一标识符。
- orderId
 此订单的唯一标识符。
- callback
 回调函数。
- optionalParams (optional)
 其他字段,如 correlationId。
如果付款人的 3DS 身份验证可用,回调函数的 data 参数中将返回以下字段。 否则,响应将包含错误。
- data.restApiResponse
 来自 Initiate Authentication REST API 调用的原始响应。
- data.correlationId
 用于发起 Initiate Authentication REST API 调用的最后一个关联 ID。 您可以使用它匹配响应和请求。
- data.gatewayRecommendation
 根据您或您的 PSP 配置的 3DS 交易筛选规则,对您的后续操作给出的建议:- PROCEED: 您可以继续执行步骤 4 来对付款人进行身份验证。
- RESUBMIT_WITH_ALTERNATIVE_PAYMENT_DETAILS: 要求付款人提供备用付款详细信息,如新卡或其他付款方式,然后使用新详细信息重新提交请求。 不要重新提交相同的请求。
 
- data.authenticationVersion
 如果身份验证可用,则返回 3DS2。
var optionalParams = {
    sourceOfFunds: {
        type: "CARD"
    },
    order: {
        walletProvider: "MASTERPASS_ONLINE"
    }
};
ThreeDS.initiateAuthentication({orderId}, {transactionId}, function (data) {
    if (data && data.error) {
        var error = data.error;
        //Something bad happened, the error value will match what is returned by the Authentication API
        console.error("error.code : ", error.code);
        console.error("error.msg : ", error.msg);
        console.error("error.result : ", error.result);
        console.error("error.status : ", error.status);
    } else {
        console.log("After Initiate 3DS ", data);
        //data.response will contain information like gatewayRecommendation, authentication version, and so on.
        console.log("REST API raw response ", data.restApiResponse);
        console.log("Correlation Id", data.correlationId);
        console.log("Gateway Recommendation", data.gatewayRecommendation);
        console.log("HTML Redirect Code", data.htmlRedirectCode);
        console.log("Authentication Version", data.authenticationVersion);
        switch (data.gatewayRecommendation) {
            case "PROCEED":
                authenticatePayer();//merchant's method
                break;
            case "RESUBMIT_WITH_ALTERNATIVE_PAYMENT_DETAILS":
               tryOtherPayment();//Card does not support 3DS and transaction filtering rules require 3DS on this transaction: Ask the payer to select a different payment method.
                break;
        }
    }
}, optionalParams);
		
		{ 
   "authentication":{ 
      "3ds2":{ 
         "methodCompleted":false,
         "methodSupported":"SUPPORTED"
      },
      "redirect":{ 
         "customized":{ 
            "3DS":{ 
               "methodPostData":"eyJ0aHJlZURTTWV0aG9kTm90aWZpY2F0aW9uVVJMIjoiaHR0cHM6Ly9xYTA0LmdhdGV3YXkubWFzdGVyY2FyZC5jb20vY2FsbGJhY2tJbnRlcmZhY2UvZ2F0ZXdheS80ZjNmMGQyMjM5NzQwODE2OWIwMWFiYzg2OTQyZTY5NzBmODA2M2M0MDU4ZjAzNjNlOTFlMmJiOTNkOTA0NzU3IiwidGhyZWVEU1NlcnZlclRyYW5zSUQiOiJhYWY5YjU5ZC0yZTA0LTRjZDUtOTQzOC01OGU4MGEzNzBiNWEifQ==",
               "methodUrl":"<method_url>"
            }
         }
      },
      "redirectHtml":"<div id=\"initiate3dsSimpleRedirect\" xmlns=\"http://www.w3.org/1999/html\"> <iframe id=\"methodFrame\" name=\"methodFrame\" height=\"100\" width=\"200\" > </iframe> <form id =\"initiate3dsSimpleRedirectForm\" method=\"POST\" action=\"https://<host_name>/acs/v2/method\" target=\"methodFrame\"> <input type=\"hidden\" name=\"threeDSMethodData\" value=\"eyJ0aHJlZURTTWV0aG9kTm90aWZpY2F0aW9uVVJMIjoiaHR0cHM6Ly9xYTA0LmdhdGV3YXkubWFzdGVyY2FyZC5jb20vY2FsbGJhY2tJbnRlcmZhY2UvZ2F0ZXdheS80ZjNmMGQyMjM5NzQwODE2OWIwMWFiYzg2OTQyZTY5NzBmODA2M2M0MDU4ZjAzNjNlOTFlMmJiOTNkOTA0NzU3IiwidGhyZWVEU1NlcnZlclRyYW5zSUQiOiJhYWY5YjU5ZC0yZTA0LTRjZDUtOTQzOC01OGU4MGEzNzBiNWEifQ==\" /> </form> <script>document.getElementById(\"initiate3dsSimpleRedirectForm\").submit();</script> </div>",
      "version":"3DS2"
   },
   "order":{
      "currency":"AUD",
      "status":"AUTHENTICATION_INITIATED"
   },
   "response":{
      "gatewayCode":"AUTHENTICATION_IN_PROGRESS",
      "gatewayRecommendation":"PROCEED"
   },
   "result":"SUCCESS",
   "sourceOfFunds":{
      "provided":{
         "card":{
            "number":"512345xxxxxx0008"
         }
      },
      "type":"CARD"
   },
   "transaction":{
      "authenticationStatus":"AUTHENTICATION_AVAILABLE"
   },
   "version":"72"
}
步骤 4: 对付款人进行身份验证
如果 INITIATE AUTHENTICATION 响应指示身份验证可用 (transaction.authenticationStatus=AUTHENTICATION_AVAILABLE),您可以对付款人进行身份验证。 当付款人单击结账页面的“立即付款”按钮时,调用 authenticatePayer() 函数。 此函数调用付款人身份验证 API 的 AUTHENTICATE PAYER 请求。
您必须通过提供以下强制参数来调用 authenticatePayer() 函数:
- orderId
 与您在前面的- initiateAuthentication()函数中使用的相同的订单 ID。
- transactionId
 与您在前面的- initiateAuthentication()函数中使用的交易 ID 相同。
- callback
 回调函数。
- optionalParams(Optional)
 其他 AUTHENTICATE PAYER 请求字段,如 billing 和 shipping。
回调函数的 data 参数中返回以下字段:
- data.restApiResponse
 来自 AUTHENTICATE PAYER 请求的原始响应。 返回的字段取决于流(无障碍或质询)以及为会话定义的身份验证通道。 对于经过会话身份验证的请求,将对响应进行筛选以删除与付款人无关的数据,然后仅返回加入白名单的字段。 有关详细信息,请参阅会话基础知识。
- data.correlationId
 用于发出 AUTHENTICATE PAYER 请求的最后一个关联 ID。 您可以使用它匹配响应和请求。
- data.gatewayRecommendation
- PROCEED:您可以继续完成身份验证流程(质询流)或继续完成付款(无障碍流)。 如果对付款的授权成功,继续过账资金,如果适用,则发货。
- DO_NOT_PROCEED_ABANDON_ORDER:不要再次提交相同的请求。 PSP、组织或发卡机构要求您放弃订单。
- RESUBMIT_WITH_ALTERNATIVE_PAYMENT_DETAILS:要求付款人提供备用付款详细信息(例如,新卡或其他付款方式),然后使用新详细信息重新提交请求。 不要重新提交相同的请求。
- data.htmlRedirectCode
 重定向代码。 如果- gatewayRecommendation为 PROCEED,将此代码插入显示给付款人的页面。
对您的后续操作给出的建议(根据您或 your payment service provider 配置的 3DS 交易筛选规则):
如果网关建议您 PROCEED:
- 如果 ACS 为付款人选择了无障碍流,它会将付款人的浏览器直接重定向回您的网站,您可以继续执行步骤 5 向网关提交后续付款。 网关将获取与付款相关的身份验证数据,并确保仅在所有 3DS 交易筛选规则(由您或 your payment service provider 配置)都通过的情况下付款才会被处理。
- 如果 ACS 为付款人选择了质询流,使用响应中提供的 htmlRedirectCode将付款人重定向到 ACS。 质询完成后,ACS 会将付款人重定向回您的网站,并触发包含orderId、transactionId、gatewayRecommendation和restApiResponse字段的回调。 根据gatewayRecommendation字段确定质询的结果。 对于authenticatePayer()函数响应,适用与上述相同的规则。 如果建议是 PROCEED,继续执行步骤 5。
var optionalParams = {
    fullScreenRedirect: true,
    billing: {
        address: {
            city: "London",
            country: "GBR"
        }
    }
};
 
ThreeDS.authenticatePayer({orderId}, {transactionId}, function (data) {
    if (!data.error) {
        //data.response will contain all the response payload from the AUTHENTICATE_PAYER call.
        console.log("REST API response ", data.restApiResponse);
        console.log("HTML redirect code", data.htmlRedirectCode);
        displayReceipt(data);
    }
}, optionalParams);
 
 
function displayReceipt(apiResponse) {
    var responseBody = {
        "apiResponse": apiResponse
    };
 
    var xhr = new XMLHttpRequest();
    xhr.open('PUT', '3dsreceipt', true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onreadystatechange = function () {
        if (xhr.readyState == XMLHttpRequest.DONE) {
            document.documentElement.innerHTML = this.response;
        }
    }
    xhr.send(JSON.stringify(responseBody));
}
			
    {
   "authentication":{
      "3ds":{
         "transactionId":"6dfa4509-1bf2-425b-965b-d44dd11f5f91"
      },
      "3ds2":{
         "3dsServerTransactionId":"8c4a911c-289a-46c2-a615-887e1cc01a6a",
         "acsTransactionId":"2a8234c9-e8ac-449d-a693-97a113b491fc",
         "directoryServerId":"A000000004",
         "dsTransactionId":"6dfa4509-1bf2-425b-965b-d44dd11f5f91",
         "methodCompleted":false,
         "methodSupported":"SUPPORTED",
         "protocolVersion":"2.1.0",
         "requestorId":"test2ID",
         "requestorName":"test2Name",
         "transactionStatus":"C"
      },
      "method":"OUT_OF_BAND",
      "payerInteraction":"REQUIRED",
      "redirect":{
         "customized":{
            "3DS":{
               "acsUrl":"https://<host_name>/acs/v2/prompt",
               "cReq":"eyJ0aHJlZURTU2VydmVyVHJhbnNJRCI6ImNhODM1ZDQxLTBlMDktNGI3OC1hNmUyLWQwZjJiNjFlZjBjOCJ9"
            }
         },
         "domainName":"<domain_name>"
      },
      "redirectHtml":"<div id=\"threedsChallengeRedirect\" xmlns=\"http://www.w3.org/1999/html\"> <form id =\"threedsChallengeRedirectForm\" method=\"POST\" action=\"https://<host_name>/acs/v2/prompt\" target=\"challengeFrame\"> <input type=\"hidden\" name=\"creq\" value=\"eyJ0aHJlZURTU2VydmVyVHJhbnNJRCI6ImNhODM1ZDQxLTBlMDktNGI3OC1hNmUyLWQwZjJiNjFlZjBjOCJ9\" /> </form> <iframe id=\"challengeFrame\" name=\"challengeFrame\" width=\"100%\" height=\"100%\" ></iframe> <script id=\"authenticate-payer-script\"> var e=document.getElementById(\"threedsChallengeRedirectForm\"); if (e) { e.submit(); e.remove(); } </script> </div>",
      "version":"3DS2"
   },
   "correlationId":"test",
   "device":{
      "browser":"MOZILLA",
      "ipAddress":"127.0.0.1"
   },
   "merchant":"TEST_3DS2-1",
   "order":{
      "amount":100,
      "authenticationStatus":"AUTHENTICATION_PENDING",
      "creationTime":"2021-04-13T02:22:59.113Z",
      "currency":"AUD",
      "id":"807a01b6-e6c8-4aa7-b8da-799bfff89496",
      "lastUpdatedTime":"2021-04-13T02:44:07.161Z",
      "merchantCategoryCode":"1234",
      "status":"AUTHENTICATION_INITIATED",
      "totalAuthorizedAmount":0,
      "totalCapturedAmount":0,
      "totalRefundedAmount":0,
      "valueTransfer":{
         "accountType":"NOT_A_TRANSFER"
      }
   },
   "response":{
      "gatewayCode":"PENDING",
      "gatewayRecommendation":"PROCEED"
   },
   "result":"PENDING",
   "sourceOfFunds":{
      "provided":{
         "card":{
            "expiry":{
               "month":"1",
               "year":"39"
            },
            "number":"512345xxxxxx0008",
            "scheme":"MASTERCARD"
         }
      },
      "type":"CARD"
   },
   "timeOfLastUpdate":"2021-04-13T02:44:07.161Z",
   "timeOfRecord":"2021-04-13T02:22:59.113Z",
   "transaction":{
      "acquirer":{
         "merchantId":"99554411"
      },
      "amount":100,
      "authenticationStatus":"AUTHENTICATION_PENDING",
      "currency":"AUD",
      "id":"42090084",
      "type":"AUTHENTICATION"
   },
   "version":"60"
}
高级集成
付款人的浏览器在完成 authenticatePayer() 方法后向您的网站提交的请求是参数化的,允许您确定身份验证结果。 单独的身份验证字段(例如  authentication.3ds2.transactionStatus.data)在高级集成中或者您需要在通过另一个网关处理的付款中提供身份验证数据时很有用。 有关更多详细信息,请参见如何实现高级付款会话集成?
步骤 5: 在付款中使用身份验证
当 authenticatePayer() 函数的结果表明您可以继续处理付款 (gatewayRecommendation=PROCEED) 时,您可以发起 WS API AUTHORIZE 或 PAY 操作。 除了标准字段外,您还必须提供以下字段:
- order.id
 使用您在initiateAuthentication()和authenticatePayer()函数中提供的相同orderId。
- authentication.transactionId: 使用与您在 initiateAuthentication()和authenticatePayer()函数中提供的相同transactionId。 您不需要在authentication对象中包含任何字段,因为网关使用 authentication.transactionId 来查找您要求网关执行身份验证时它存储的身份验证结果。 网关将需要的信息传送到收单行。
| URL | https://gateway-emea.americanexpress.com/api/rest/version/<version>/merchant/<your_merchant_ID>/order/<your_order_ID>/transaction/<your_transaction_ID> | 
| HTTP 方法 | PUT | 
 
   {
      "apiOperation":"PAY",
      "authentication":{
          "transactionId":"<your_transaction_ID>"
      },
      "session": {
          "id": "<your_session_id>"
      },
      "transaction":{
          "reference":"<your_order_ID>"
      }
  } 
  
	
	{
   "authentication":{
      "3ds":{
         "acsEci":"02",
         "authenticationToken":"kHyn+7YFi1EUAREAAAAvNUe6Hv8=",
         "transactionId":"39c25b96-7bc3-4586-bee8-056479fed3af"
      },
      "3ds2":{
         "dsTransactionId":"39c25b96-7bc3-4586-bee8-056479fed3af",
         "protocolVersion":"2.1.0",
         "transactionStatus":"Y"
      },
      "transactionId":"249213216",
      "version":"3DS2"
   },
   "authorizationResponse":{
      "posData":"1605S0100130",
      "transactionIdentifier":"TidTest"
   },
   "device":{
      "browser":"MOZILLA",
      "ipAddress":"127.0.0.1"
   },
   "gatewayEntryPoint":"WEB_SERVICES_API",
   "merchant":"TEST_3DS2-1",
   "order":{
      "amount":100.00,
      "authenticationStatus":"AUTHENTICATION_SUCCESSFUL",
      "chargeback":{
         "amount":0,
         "currency":"AUD"
      },
      "creationTime":"2021-04-13T02:11:06.102Z",
      "currency":"AUD",
      "id":"807a01b6-e6c8-4aa7-b8da-799bfff89496",
      "lastUpdatedTime":"2021-04-13T02:11:57.049Z",
      "merchantAmount":100.00,
      "merchantCategoryCode":"1234",
      "merchantCurrency":"AUD",
      "reference":"807a01b6-e6c8-4aa7-b8da-799bfff89496",
      "status":"CAPTURED",
      "totalAuthorizedAmount":100.00,
      "totalCapturedAmount":100.00,
      "totalRefundedAmount":0.00
   },
   "response":{
      "acquirerCode":"00",
      "gatewayCode":"APPROVED"
   },
   "result":"SUCCESS",
   "sourceOfFunds":{
      "provided":{
         "card":{
            "brand":"MASTERCARD",
            "expiry":{
               "month":"1",
               "year":"39"
            },
            "fundingMethod":"CREDIT",
            "issuer":"<issuer>",
            "number":"512345xxxxxx0008",
            "scheme":"Mastercard",
            "storedOnFile":"NOT_STORED"
         }
      },
      "type":"CARD"
   },
   "timeOfLastUpdate":"2021-04-13T02:11:57.049Z",
   "timeOfRecord":"2021-04-13T02:11:56.973Z",
   "transaction":{
      "acquirer":{
         "batch":1,
         "id":"<acquirer_id>",
         "merchantId":"99554411"
      },
      "amount":100.00,
      "authenticationStatus":"AUTHENTICATION_SUCCESSFUL",
      "authorizationCode":"028941",
      "currency":"AUD",
      "id":"1",
      "receipt":"1908266016",
      "reference":"807a01b6-e6c8-4aa7-b8da-799bfff89496",
      "source":"INTERNET",
      "stan":"496",
      "terminal":"1234",
      "type":"PAYMENT"
   },
   "version":"60"
}