3DS Flex - Worldpay with Cardinal

3DS Flexis the most advanced product on the market for 3-D Secure (3DS). It combines the new standard of 3DS2 with traditional 3DS allowing for a single integration.

To make use of the benefits of 3DS Flex you must integrate 3DS2. This guide provides steps on how to achieve this.

Note:

  • You must be setup for 3DS Flex, before using it. For more information, contact your Relationship Manager or Implementation Manager.
  • This guide is updated as new features are released.

How Worldpay provides 3DS2

Worldpay have a relationship with Cardinal Commerce whereby your website integrates to Cardinal Commerce's 3DS2 authentication service for Device Data Collection and challenge flows. The rest of the process is managed by Worldpay.

3DS2 flow

Flow

Device Data Collection

  1. Create a JSON Web Token (JWT) on your server that is passed to the browser. When the shopper has entered a card number, start the Device Data Collection (DDC).
  2. Include a hidden DDC iframe, with the DDC JWT and the shopper's card number, on your page.
  3. Post the form to Cardinal Commerce's DDC endpoint.
  4. You receive a notification (on the shopper's browser) that DDC is complete. DDC returns a JavaScript message event, containing a SessionId.

Initial Payment Request

  1. Take the SessionId returned from the Device Data Collection, and submit it as the dfReferenceId in your XML payment request.
  2. An authentication request is submitted to Cardinal Commerce to determine if a challenge is required. The possible outcomes are:
    • Frictionless Flow - An authorisation is attempted (Proceed to Step 2 inSecond Payment Request).
    • Challenge Required - Proceed to 3DS2 Challenge

Payment requests with JavaScript not enabled

When a payment request is sent from a browser that cannot run JavaScript,DDCcannot be performed. Without DDC, 3DS2 cannot be automatically performed. To initiate 3DS2 you must add the attribute javascriptEnabled="false" within the element"additional3DSData"

You must also add the child element <browserLanguage> to your payment request. This additional information is included in the request for 3DS2 authentication:

Copied!
<shopper>
  <shopperEmailAddress>a.shopper@worldpay.com</shopperEmailAddress>
  <browser>
    <acceptHeader>text/html</acceptHeader>
    <userAgentHeader>Mozilla/5.0…</userAgentHeader>
    <browserLanguage>en-GB</browserLanguage>
  </browser>
</shopper>

3DS2 logic

JavaScript not enabled

IfDDCis successful but you have indicated JavaScript is not enabled, an error message is returned. Your payment request is then submitted with 3DS2 default values.

3DS2 Challenge

  1. Extract the data from the challenge response.
  2. Create a challenge JWT on your server that is passed to the browser. Include a ReturnUrl and the specified values returned in the XML challenge response.
  3. Include a challenge iframe, with the challenge JWT and the optional merchant data (MD), on your page.
  4. Automatically submit this form to the Cardinal Commerce endpoint.
  5. The shopper completes the challenge in the iframe and is redirected to the ReturnUrl.

Second Payment Request

  1. Submit a second XML request, including the shopper session id and machine cookie from the initial XML request.
  2. If authentication is successful, Worldpay attempts authorisation.

Device Data Collection (DDC)

JWT creation

All requests to Cardinal Commerce from the shopper's browser must be authenticated using a JSON Web Token (JWT). Providing this gives the shopper's browser access to resources to complete DDC and Challenges. You must create all JWT's on your server and not in the browser. This is because the JWT MAC Key used in JWT creation must only be known to you, Worldpay and Cardinal Commerce.

Best practice: We strongly recommend that you use a third-party library to create the JWT in its entirety.

JWT structure

JWT's consist of three parts (Header, Body and MAC). They are described below:

Header

The purpose of the header is to identify that the body is a JWT and to specify the message authentication algorithm. This is used to create the Message Authentication Code (MAC). The following algorithms are supported:

  • HS256 (HMAC with SHA256)
  • HS512 (HMAC with SHA512)

Example Header:

Copied!
{
  "typ":"JWT",
  "alg":"HS256"
}

Body

A JSON object that contains the claims (name-value pairs) being sent from one party to another. The body must only contain the claims below, adding additional claims results in 400 Bad Request response.

Claim NameM/ODescription
jtiMJWT Id - A unique identifier for this JWT. This field must be set to a random UUID each time a JWT is generated.
iatMIssued At - The epoch time (in seconds - 10 digits) of when the JWT was generated. Valid for two hours.
issMIssuer - An identifier for who is issuing the JWT. Use "5bd9e0e4444dce153428c940" in test. We provide values for live.
expOExpiration - The numeric epoch time (in seconds - 10 digits) that the JWT should be considered expired. Anything over two hours in the future is ignored.
OrgUnitIdMOrganisational Unit Id - An identity associated with your account. Use "5bd9b55e4444761ac0af1c80" in test. We provide the values for live.

Example Body:

Copied!
{
  "jti": "69adc185-1748-4525-9ef9-43f259a1c2d6",
  "iat": 1548838855,
  "iss": "5bd9e0e4444dce153428c940",
  "exp": 1548838900,
  "OrgUnitId": "5bd9b55e4444761ac0af1c80"
}

MAC

A base64url encoded hash value of the header and payload combined with a JWT MAC Key. This is used to verify that the contents of the JWT have not been tampered with. Authentication codes are verified by the consumer by recreating the MAC from the JWT header, body and JWT MAC Key.

AttributeDescription
JWT MAC KeyPass this as a string and not a number. Use fa2daee2-1fbb-45ff-4444-52805d5cd9e0 in test. We provide the values for live

DDC initiation

DDC requires you to change your website to run JavaScript from Cardinal Commerce on your page. The JavaScript runs on the shopper's browser to collect device data as part of the authentication process regardless whether 3DS1 or 3DS2 is used.

Place an invisible iframe on your page. Create a form in the iframe using the POST method with action="https://secure-test.worldpay.com/shopper/3ds/ddc.html". Include the field JWT and Bin as described below. Do not submit this form until the card can no longer be changed.

FieldDescription
JWTThe authentication token, as describedabove.
BinThe card number (PAN). Minimum of first six digits. Fortokenisationuse the value of <bin>

Here's an example of one way to do it:

Copied!
<iframe height="1" width="1" style="display: none;">
  <!-- This is a Cardinal Commerce URL in live. -->
  <form id="collectionForm" method="POST" action="https://secure-test.worldpay.com/shopper/3ds/ddc.html">
    <input type="hidden" name="Bin" value="4444333322221111" />
    <input type="hidden" name="JWT" value="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2OWFkYzE4NS0xNzQ4LTQ1MjUtOWVmOS00M2YyNTlhMWMyZDYiLCJpYXQiOjE1NDg4Mzg4NTUsImlzcyI6IjViZDllMGU0NDQ0ZGNlMTUzNDI4Yzk0MCIsIk9yZ1VuaXRJZCI6IjViZDliNTVlNDQ0NDc2MWFjMGFmMWM4MCJ9.qTyYn4rItMMNdnh6ouqW6ZmcCNzaG9JI_GdWGIaq6rY" />
  </form>
  <script>
    window.onload = function() {
      document.getElementById('collectionForm').submit();
    }
  </script>
</iframe>

DDC Outcome

You are notified via a JavaScript postMessage that DDC has been completed. Your website must listen for this notification which contains the following fields:

NameValue
MessageTypeprofile.completed
SessionIdUUID, not present or undefined
Statustrue or false

There are three possible scenarios:

StatusAction
trueSend SessionId as dfReferenceId in initial payment request.
falseSessionId is empty. Either retry DDC or send empty dfReferenceId. This downgrades authentication to 3DS1.
No callbackEither retry DDC or send empty dfReferenceId. This downgrades authentication to 3DS1.

Example postMessage:

Copied!
{
    "MessageType": "profile.completed",
    "SessionId": "d3197c02-6f63-4ab2-801c-83633d097e32",
    "Status": true
}

Here's some example JavaScript code for reference:

Copied!
window.addEventListener("message", function(event) {
   //This is a Cardinal Commerce URL in live.
   if (event.origin === "https://secure-test.worldpay.com/") {
       var data = JSON.parse(event.data);
       console.warn('Merchant received a message:', data);
       if (data !== undefined && data.Status) {
           // Extract the value of SessionId for onward processing.
       }
   }
}, false);

Extract the SessionId, if returned, and retain it for use in your initial payment request.

Initial XML Payment Request

Create theXML authorisation request. Submit all available information to increase the chance of a frictionless flow and higher authorisation rate.

Note: We recommend providing an orderCode of less than 50 characters for 3DS Flex requests. Where the orderCode is longer than 50 characters, we will truncate it for downstream processing. This adjusted orderCode is only be used in Cardinal's systems.
This doesn't impact the orderCode that is stored internally, the orderCode that is returned to you, or the orderCode that is visible in the Merchant Admin Interface (MAI).

  1. Address data (Mandatory)
  2. additional3DSData (Mandatory)
  3. riskData (Recommended to increase chances of a frictionless flow)

1. Address Data

Submit the <cardAddress> or <billingAddress> element. We recommend you send this, as this is required by the schemes.

Submit the <shippingAddress> element in your request if you want to submit the shopper's delivery address.


<cardAddress>,<billingAddress> and <shippingAddress> contain:

ElementM/C/ODescription
<cardHolderName>MandatoryAccount holder name on card or account.
<address1>ConditionalBilling contact Address line 1 .
Optional unless <city> is supplied.
<address2>OptionalBilling contact Address line 2.
<address3>OptionalBilling contact Address line 3.
<postalCode>MandatoryBilling contact ZIP / Postal Code.
Example “CB4 0WE”
<city>ConditionalBilling contact City.
Optional unless <address1> is supplied
<state>OptionalState should only be populated when Billing or Shipping address is domiciled within the United States or China. In this case the value is two-characters long as defined in the country subdivision code within the ISO-3611-2 specification. (Note: Only provide the two characters after “US-" or "CN-" for example “FL” for Florida and "BJ" for Beijing, etc.)
<countryCode>MandatoryBilling contact ISO-3166 Country Code. Must be upper case.
Example “GB”.
For details, seeISO Country Codes

Submit the shopper's details:

ElementM/C/ODescription
<shopperEmailAddress>MandatoryShopper's email address
<shopperIPAddress>MandatoryShopper's IP address

2. Specifying Additional 3DS Data

There are three attributes inside <additional3DSData>:

AttributesM/ODescription
dfReferenceIdMandatoryThe SessionId returned in the Javascript postMessage after DDC is complete.
challengePreferenceOptionalPossible Values:
  • noPreference (default) - You have no preference whether a challenge should be performed.
  • noChallengeRequested - You prefer that no challenge should be performed.
  • challengeRequested - You prefer that a challenge should be performed.
  • challengeMandated - There are local or regional mandates that mean that a challenge must be performed.

Note: The interpretation of this field varies from issuer to issuer, so Worldpay cannot guarantee any particular behaviour on their part as a result of you setting this field.

challengeWindowSizeOptionalChallenge Window size the issuer should use to display the challenge. Possible Values:
  • fullPage
  • 250x400
  • 390x400 (default)
  • 500x600
  • 600x400

3. Adding Risk Data

Provide additional information in the <riskData> element to increase the chances that the shopper won't be challenged. <riskData> contains three child elements:

Information about the shopper and how they are authenticating with Worldpay.

The <authenticationRiskData> element contains:

ElementDescriptionType
authenticationTimestampDate and time in UTC of the shopper's authentication.<date> which must include the attributes: dayOfMonth, month, year, hour, minute and second
AttributeDescriptionType
authenticationMethodMechanism used by the shopper to authenticate with you.Possible values:
  • guestCheckout - the shopper has not been authenticated
  • localAccount - you have authenticated the shopper using your own systems
  • federatedAccount - you have authenticated the shopper using a Federated ID
  • fidoAuthenticator - you have authenticated the shopper using FIDO Authenticator
  • issuerCredentials- you have authenticated the shopper using issuer credentials
  • thirdPartyAuthentication - you have authenticated the shopper using third-party authentication

Information about the shopper's account with you.

The <shopperAccountRiskData> element contains:

ElementDescriptionType
<shopperAccountCreationDate>Date that the shopper opened the account with the merchant.WPG XML date format: specify sub-attributes dayOfMonth, month, year
<shopperAccountModificationDate>Date that the shopper's account with the merchant was last changed, including Billing or Shipping address, new payment account, or new user(s) added.WPG XML date format: specify sub-attributes dayOfMonth, month, year
<shopperAccountPasswordChangeDate>Date that shopper's account with the merchant had a password change or account reset.WPG XML date format: specify sub-attributes dayOfMonth, month, year.
<shopperAccountShippingAddressFirstUseDate>Indicates when the shipping address used for the transaction was first used.WPG XML date format: specify sub-attributes dayOfMonth, month, year.
<shopperAccountPaymentAccountFirstUseDate>Date the payment account was added to the shopper account.WPG XML date format: specify sub-attributes dayOfMonth, month, year.
AttributeDescriptionType
transactionsAttemptedLastDayNumber of transactions (successful and abandoned) for this shopper account with the merchant across all payment accounts in the previous 24 hours.Integer >=0
transactionsAttemptedLastYearNumber of transactions (successful and abandoned) for this shopper account with the merchant across all payment accounts in the previous year.Integer >=0
purchasesCompletedLastSixMonthsNumber of purchases with this shopper account during the previous six months.Integer >=0
addCardAttemptsLastDayNumber of Add Card attempts in the last 24 hours.Integer >=0
previousSuspiciousActivityIndicates whether the merchant has experienced suspicious activity (including previous fraud) on the shopper account.Boolean (true or false)
shippingNameMatchesAccountNameIndicates if the cardholder name on the account is identical to the shipping name used for this transaction.Boolean (true or false)
shopperAccountAgeIndicatorIndicates how long the shopper had the account with the merchant.Possible Values: noAccount, createdDuringTransaction, lessThanThirtyDays, thirtyToSixtyDays, moreThanSixtyDays
shopperAccountChangeIndicatorLength of time since the last change to the shopper's account. This includes billing or shipping address, new payment methods or new users added.Possible values: changedDuringTransaction, lessThanThirtyDays, thirtyToSixtyDays, moreThanSixtyDays
shopperAccountPasswordChangeIndicatorIndicates when the shopper's account password was last changed or reset.Possible Values: noChange, changedDuringTransaction, lessThanThirtyDays, thirtyToSixtyDays, moreThanSixtyDays
shopperAccountShippingAddressUsageIndicatorIndicates when the shipping address was first used.Possible Values: thisTransaction, lessThanThirtyDays, thirtyToSixtyDays, moreThanSixtyDays
shopperAccountPaymentAccountIndicatorIndicates when the payment account was first used.Possibe Values: noAccount, duringTransaction, lessThanThirtyDays, thirtyToSixtyDays, moreThanSixtyDays

Information about the order.

The <transactionRiskData> element contains:

ElementDescriptionType
<transactionRiskDataGiftCardAmount>For prepaid or gift card purchase, the purchase amount total of prepaid or gift card(s) in major units (for example, USD 123.45 is 123Amount (value, currencyCode, exponent, <debitCreditIndicator>)
<transactionRiskDataPreOrderDate>For a pre-ordered purchase, the expected date that the merchandise is available.Date

AttributeDescriptionType
shippingMethodIndicates shipping method chosen for the transaction.Possible Values:
  • shipToBillingAddress - Ship to shopper billing address
  • shipToVerifiedAddress - Ship to another verified address on file with merchant
  • shipToOtherAddress - Ship to address that is different than billing address
  • shipToStore - Ship to store (store address should be populated on request)
  • digital- Digital goods
  • unshippedTravelOrEventTickets - Travel and event tickets, not shipped
  • other - Other
    deliveryTimeframeIndicates the delivery timeframe.Possible values:
    • electronicDelivery - for goods that are only delivered electronically
    • sameDayShipping - for goods shipped the same day
    • overnightShipping - for goods shipped overnight
    • otherShipping - two-day or more shipping.
    deliveryEmailAddressFor electronically delivered goods only. Email address to which the merchandise was delivered.email address
    reorderingPreviousPurchasesIndicates whether the shopper is reordering previously purchased merchandise.Boolean (true or false)
    preOrderPurchaseIndicates whether shopper is placing an order with a future availability or release date.Boolean (true or false)
    giftCardCountTotal count of individual prepaid gift cards purchased.Integer >=0

    Complete Initial Payment Request XML Example

    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//Worldpay//DTD Worldpay PaymentService v1//EN"
      "http://dtd.worldpay.com/paymentService_v1.dtd">
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
      <submit>
        <order orderCode="YOUR_ORDER_CODE"> 
          <description>YOUR DESCRIPTION</description>
          <amount value="2000" currencyCode="EUR" exponent="2"/>
          <orderContent>
            <![CDATA[]]>
          </orderContent>
          <paymentDetails>
            <CARD-SSL>
              <cardNumber>4444333322221111</cardNumber>
              <expiryDate>
                <date month="01" year="2020"/>
              </expiryDate>
              <cardHolderName>3DS_V2_CHALLENGE_IDENTIFIED</cardHolderName>
              <cvc>123</cvc>
              <cardAddress>
                <address>
                  <address1>Worldpay</address1>
                  <address2>270-289 The Science Park</address2>
                  <address3>Milton Road</address3>
                  <postalCode>CB4 0WE</postalCode>
                  <city>Cambridge</city>
                  <countryCode>GB</countryCode>
                </address>
              </cardAddress>
            </CARD-SSL>
            <session shopperIPAddress="127.0.0.1" id="SESSION_ID"/> <!--Session id must be unique -->
          </paymentDetails>
          <shopper>
            <shopperEmailAddress>jshopper@myprovider.com</shopperEmailAddress>
            <browser>
              <acceptHeader>text/html</acceptHeader>
              <userAgentHeader>Mozilla/5.0 ...</userAgentHeader>
            </browser>
          </shopper>
          <!-- Optional Risk Data -->
          <riskData>
            <authenticationRiskData authenticationMethod="localAccount">
              <authenticationTimestamp><date second="01" minute="02" hour="03" dayOfMonth="01" month="06" year="2019"/></authenticationTimestamp>
            </authenticationRiskData>
            <shopperAccountRiskData
              transactionsAttemptedLastDay="1"
              transactionsAttemptedLastYear="100"
              purchasesCompletedLastSixMonths="50"
              addCardAttemptsLastDay="1"
              previousSuspiciousActivity="true"
              shippingNameMatchesAccountName="true"
              shopperAccountAgeIndicator="lessThanThirtyDays"
              shopperAccountChangeIndicator="lessThanThirtyDays"
              shopperAccountPasswordChangeIndicator="noChange"
              shopperAccountShippingAddressUsageIndicator="thisTransaction"
              shopperAccountPaymentAccountIndicator="lessThanThirtyDays">          
              <shopperAccountCreationDate><date dayOfMonth="01" month="02" year="2003"/></shopperAccountCreationDate>
              <shopperAccountModificationDate><date dayOfMonth="02" month="03" year="2004"/></shopperAccountModificationDate>
              <shopperAccountPasswordChangeDate><date dayOfMonth="03" month="04" year="2005"/></shopperAccountPasswordChangeDate>
              <shopperAccountShippingAddressFirstUseDate><date dayOfMonth="04" month="05" year="2006"/></shopperAccountShippingAddressFirstUseDate> 
              <shopperAccountPaymentAccountFirstUseDate><date dayOfMonth="05" month="06" year="2007"/></shopperAccountPaymentAccountFirstUseDate>
            </shopperAccountRiskData>
            <transactionRiskData
              shippingMethod="shipToBillingAddress"
              deliveryTimeframe="overnightShipping"
              deliveryEmailAddress="sp@worldpay.com"
              reorderingPreviousPurchases="true"
              preOrderPurchase="false"
              giftCardCount="1">
            <transactionRiskDataGiftCardAmount><amount value="1" currencyCode="EUR" exponent="2"/></transactionRiskDataGiftCardAmount>
              <transactionRiskDataPreOrderDate><date dayOfMonth="06" month="07" year="2008"/></transactionRiskDataPreOrderDate>
            </transactionRiskData>
          </riskData>
          <!-- Additional 3DS data that you must provide to us -->
          <additional3DSData
            dfReferenceId="1f1154b7-620d-4654-801b-893b5bb22db1"
            challengeWindowSize="390x400"
            challengePreference="challengeMandated"/>
        </order>
      </submit>
    </paymentService>

    Warning: If you are not setup for 3DS Flex, you receive an error code "13 - merchant not setup in live" in your response. Contact your Relationship Manager to be enabled for 3DS Flex.

    We pass the information to Cardinal Commerce, who match the DDC to the payment request using the dfReferenceId value. This information is sent to the issuer who then establishes whether the payment requires additional authentication (i.e. a challenge) or not.

    The response to your authorisation request is either a standardauthorisation responsewhich ends the payment flow or a 3DS challenge response.

    3DS Challenge

    If the issuer decides that the payment requires additional authentication, they signal to Worldpay that a challenge is required. You'll receive a response from us to redirect the shopper to a challenge page. The 3DS related information is inside the threeDSChallengeDetails element. The child elements of these are as follows:

    Field nameDescriptionFormat
    acsURLURL of the Issuer's Access Control System (ACS) challenge page. Pass it in the challenge JWT as Payload.ACSUrlURL
    transactionId3DSThe MPI's identity of the transaction. Add this to the challenge JWT as Payload.TransactionIdString
    threeDSVersionTells you whether a 3DS1 or 3DS2 flow is taken. This could affect what you do next in your integrationPossible Values:
    • 1.0.2
    • 2.1.0
    • 2.2.0
    payloadThis contains the information needed to initiate the Challenge. Add this to the Challenge JWT as Payload.PayloadBase64 encoded binary object

    Example response with payload:

    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN"
          "http://dtd.worldpay.com/paymentService v1.dtd">
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
        <reply>
            <orderStatus orderCode='YOUR_ORDER_CODE'>
                <challengeRequired>
                    <threeDSChallengeDetails>
                        <threeDSVersion>2.1.0</threeDSVersion>
                        <transactionId3DS>rUT8fLKDviHXr8aUn3l1</transactionId3DS>
                        <acsURL><![CDATA[https://worldpay.com]]></acsURL>
                        <payload>P.25de9db33221a55eedc6ac352b927a8c3a08d747643c592dd8f8ab7d3...</payload>
                    </threeDSChallengeDetails>
                </challengeRequired>
            </orderStatus>
        </reply>
    </paymentService>

    Extract information from the XML response and pass it back to the shopper browser to redirect the shopper to complete the authentication.

    Warning: When we reply, your system must also extract the machine cookie passed back in the HTTP header of this reply message. You must then return the corresponding cookie in the HTTP header of the second order message.

    The cookie you return is slightly different from the cookie in our reply. For example:

    • If we send the cookie as: Set-Cookie: machine=0aa20016;path=/
    • Return the cookie in the HTTP header as: Cookie: machine=0aa20016;path=/

    Redirect to 3DS2 Challenge

    As the redirect to challenge is another Cardinal Commerce page, you must create a challenge JWT on your server that is passed to the browser. Refer toJWT creationif you need to.

    The Challenge JWT contains more fields than the DDC one:

    Claim NameDescription
    jtiJWT Id - A unique identifier for this JWT. This field must be set to a random UUID each time a JWT is generated.
    iatIssued At - The epoch time (in seconds - 10 digits) of when the JWT was generated. Valid for 2 hours.
    issIssuer - An identifier of who is issuing the JWT. Use "5bd9e0e4444dce153428c940" in test. We provide values for live.
    OrgUnitIdOrganisational Unit Id is an identity associated with your account. We provide the valid values that you can use when you are set up in live. Static test value is: "5bd9b55e4444761ac0af1c80"
    ReturnUrlThe URL on your website that is invoked when the challenge is complete, see below.
    PayloadJSON container for extra data required for a challenge.
    Payload.ACSUrlThe value of the <acsURL> element from the challengeRequired response.
    Payload.PayloadThe value of the <payload> element from the challengeRequired response.
    Payload.TransactionIdThe value of the <transactionId3DS> element from the challengeRequired response.
    ObjectifyPayloadSee below.

    Depending on which JWT library you choose, you may need to escape the control characters in the JWT:

    ObjectifyPayload set to true: This is not supported by all JWT libraries, but if it is, you can write the JWT payload like this:

    Copied!
    "Payload": {
      "ACSUrl": "https://worldpay.com" ,
      "Payload": "P.25de9db33221a55eedc6ac352b927a8c3a08d747643c592dd8f8ab7d3..." ,
      "TransactionId": "sRMPWCQoQrEiVxehTnu0"
    },

    ObjectifyPayload set to false: You need to format the JWT payload with control characters escaped like this:

    Copied!
    "Payload": "{\"ACSUrl\": \"https://worldpay.com\" ,\"Payload\": \"eNpVUV1PwjAUfe4DGhKoaw==\", \"TransactionId\": \"sRMPWCQoQrEiVxehTnu0\" }",

    Here's a full example of a challenge JWT:

    Copied!
    {
        "jti": "54438b3a-bb53-12cd-8643-1536be73ff35",
        "iat": 3856729482,
        "iss": "5bd9e0e4444dce153428c940",
        "OrgUnitId": "5bd9b55e4444761ac0af1c80",
        "ReturnUrl": "https://merchant.example.com/threedschallengecomplete",
        "Payload": {
            "ACSUrl": "https://worldpay.com",
            "Payload": "P.25de9db33221a55eedc6ac352b927a8c3a08d747643c592dd8f8ab7d3...",
            "TransactionId": "sRMPWCQoQrEiVxehTnu0"
        },
        "ObjectifyPayload": true
    }

    Request Challenge Page

    Create an iframe with an automatic form post to https://secure-test.worldpay.com/shopper/3ds/challenge.html, on the page you redirect the shopper to. This is required unless you have supplied a challengeWindowSize of "fullPage". The size of the iframe depends on threeDSVersion:

    • For threeDSVersion value of 1, the size must be 390x400.
    • For threeDSVersion value of 2, match the value of challengeWindowSize supplied in the authenticate request. For more details of the support challenge windows sizes seehere.

    Example 3DS2 challenge iframe:

    Copied!
    <iframe height="250" width="400">
      <!-- This is a Cardinal Commerce URL in live -->
      <form id= "challengeForm" method= "POST" action="https://secure-test.worldpay.com/shopper/3ds/challenge.html">
        <input type="hidden" name="JWT" value= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1NDQzOGIzYS1iYjUzLTEyY2QtODY0My0xNTM2YmU3M2ZmMzUiLCJpYXQiOiIzODU2NzI5NDgyIiwiaXNzIjoiNWJkOWUwZTQ0NDRkY2UxNTM0MjhjOTQwIiwiT3JnVW5pdElkIjoiNWJkOWI1NWU0NDQ0NzYxYWMwYWYxYzgwIiwiUmV0dXJuVXJsIjoiaHR0cDovL21lcmNoYW50LmV4YW1wbGUuY29tL3RocmVlZHNjaGFsbGVuZ2Vjb21wbGV0ZSIsIlBheWxvYWQiOnsiQUNTVXJsIjoiaHR0cHM6Ly9hY3MuZXhhbXBsZS5jb20vM2RzMi9jaGFsbGVuZ2U_aWQ9MTIzNDU2Nzg5IiwiUGF5bG9hZCI6IlZHaHBjeUJwY3lCaElHSmhjMlVnTmpRZ1pXNWpiMlJsWkNCbGVHRnRjR3hsSUc5bUlHRWdNMFJUSUNKd1lYbHNiMkZrSWc9PSIsIlRyYW5zYWN0aW9uSWQiOiJzUk1QV0NRb1FyRWlWeGVoVG51MCJ9LCJPYmplY3RpZnlQYXlsb2FkIjp0cnVlfQ.3Dqjr5MuEC9AG7uvsJCft94-d70NmgR94zIeru8fAYE" />
        <input type="hidden" name="MD" value="1234567890" />
      </form>
      <script>
        window.onload = function() {
          // Auto submit form on page load
          document.getElementById('challengeForm').submit();
        }
      </script>
    </iframe>
    FieldM/ODescription
    actionMandatoryWorldpay provides a static Challenge URL when you go live. The test value is: https://secure-test.worldpay.com/shopper/3ds/challenge.html.
    JWTMandatoryThe challenge JWT that you generated on the server side.
    MDOptionalMerchant Data. This is returned as a parameter to the ReturnUrl.

    Once authentication is complete the shopper is redirected to the ReturnUrl which contains the following POST parameters:

    • ReturnUrl
    • Response
    • MD

    Second XML Payment Request

    Use this request for the ReturnUrl to trigger the second XML Payment Request (remember to also set the machine cookie on the Cookie HTTP header):

    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//Worldpay//DTD Worldpay PaymentService v1//EN"
      "http://dtd.worldpay.com/paymentService_v1.dtd" >
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">  <submit>
        <order orderCode="YOUR_ORDER_CODE"> <!--The order code supplied in the first request-->
          <info3DSecure>
            <completedAuthentication/>
          </info3DSecure>
          <session id="SESSION_ID"/> <!--The session id supplied in the first request-->
        </order>
      </submit>
    </paymentService>

    After a challenge the info3DSecure element must take a single empty child element named completedAuthentication.

    Note: The session id provided in the <session id="SESSION_ID"/> value should not be confused with the SessionId returned by the JavaScript DDC, which must be supplied in the dfReferenceId attribute of <additional3DSData> for the initial payment request only.

    Authorisation Response

    This is received because either the first authorisation request was authenticated without a challenge (frictionless flow), or a challenge has been completed by the shopper.

    The XML response from Worldpay confirms the result for the authorisation request (as per a non-authenticated authorisation), and the process ends here:

    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN" "http://dtd.worldpay.com/paymentService_v1.dtd" >
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE"> <!--The merchantCode you supplied in the order-->
      <reply>
        <orderStatus orderCode="YOUR_ORDER_CODE"> <!--The orderCode you supplied in the order-->
          <payment>
            <paymentMethod>VISA-SSL</paymentMethod>
            <amount value="5000" currencyCode="GBP" exponent="2" debitCreditIndicator="credit" />
            <lastEvent>AUTHORISED</lastEvent>
            <AuthorisationId id="123" />
            <balance accountType="IN_PROCESS_AUTHORISED">
              <amount value="5000" currencyCode="GBP" exponent="2" debitCreditIndicator="credit" />
            </balance>
            <cardNumber>4444**1111</cardNumber>
            <riskScore value="0" />
          </payment>
          <ThreeDSecureResult description="Cardholder authenticated" />
        </orderStatus>
      </reply>
    </paymentService>

    Upon receipt of this message, you complete the authorisation flow you normally would.

    Google Pay and 3DS

    SCA and PSD2 require that full PAN transactions are stepped up to 3DS to meet the minimum SCA compliance.

    You must:

    • Perform DDC without the BIN
    • Ensure your integration can handle the 3DS challenge

    DDC (Device Data Collection) without the BIN

    You must complete Device Data Collection to return the dfReferenceId. The dfReferenceId is mandatory and must be submitted in yourfirst request. With Google Pay, you do not have visibility of the card number, and you must leave the Bin value= blank.

    Copied!
    <iframe height="1" width="1" style="display: none;">
     <!-- This is a Cardinal Commerce URL in live. -->
     <form id="collectionForm" method="POST" action="https://secure-test.worldpay.com/shopper/3ds/ddc.html">
       <input type="hidden" name="Bin" value="" />
       <input type="hidden" name="JWT" value="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2OWFkYzE4NS0xNzQ4LTQ1MjUtOWVmOS00M2YyNTlhMWMyZDYiLCJpYXQiOjE1NDg4Mzg4NTUsImlzcyI6IjViZDllMGU0NDQ0ZGNlMTUzNDI4Yzk0MCIsIk9yZ1VuaXRJZCI6IjViZDliNTVlNDQ0NDc2MWFjMGFmMWM4MCJ9.qTyYn4rItMMNdnh6ouqW6ZmcCNzaG9JI_GdWGIaq6rY" />
     </form>
     <script>
       window.onload = function() {
         document.getElementById('collectionForm').submit();
       }
     </script>
    </iframe>

    An example dfReferenceId: 0_e867f4a1-5017-4a5f-9675-8776687c9f96

    First request

    Submit the dfReference in your first request.

    Copied!
    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN" "http://dtd.worldpay.com/paymentService_v1.dtd"&gt;
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
      <submit>
        <order orderCode="YOUR_ORDER_CODE">
          <description>test order</description>
          <amount value="100" currencyCode="EUR" exponent="2"/>
          <orderContent>
            <![CDATA[]]>
          </orderContent>
          <paymentDetails>
            <PAYWITHGOOGLE-SSL>
                <protocolVersion>ECv1</protocolVersion>
                <signature>MEQCIAYLy/ppXQbO2Xtdwgz0NDl5Wgs+AmULV/GHAbMAeGr0AiA6cF5dU5wCqEgAPr34RUg4BM71iO2InjxF7plv1GHn2Q==</signature>
                <signedMessage>{"encryptedMessage":"ivHWwrkaLuFK6Kxeiji9B4bbh/LOrOJlgFkpnCvi/l/fa0iYEG7eEU0D/pRqc6epm2aaYvIj7rdwCrwFHNzcOCi3JJVxUxvXJVvshB6v9Ctal/L0BBRzgIpXY11KKTTpE1V+LBC9mAwi/ibishypdAp5A2yGNONAcU7uV4jDANawmIzjYQTiYC8H71UCJLtpGVZCc0jc3ZiArTz9nBEgN3omUJfFdFm0tqJ+I27/6YOdISJoRI2WDMx01jok/GwMtWSO3UVZ2LEeN9U9yDihlEf0gqOuFYRk2vb5JuogOxLpbjjLLS6w9V6aBXVhTfm5UKjdLmkWwO+EXh+QVJsu1Hk15nAfaKTr2MBG7//lVi1x1Gj7SjQja7D+26wI+mj/rZJVVGEpgfGqb0jfrqrIpc/F9uZa/aafSlP8njEfnQwkEPq3xxuwpToyK/eMqlCwenAw8aw=","ephemeralPublicKey":"BAVydx+I5G0r6vsnxTSljjjEqFdIpWrIIKeTVd1v56qsyzchkwUzgkPP7vVFigxJ1XpQmBb/XdAl6yBzVPI+lno=","tag":"oRwGIp+UjJnVrU0Fx8tdVNInt97mIp8vTjzNb6BeJmc="}</signedMessage>
              </PAYWITHGOOGLE-SSL>
            <session shopperIPAddress="127.0.0.1" id="ssn42abcd023"/>
          </paymentDetails>
          <shopper>
            <shopperEmailAddress>ashopper@myprovider.com</shopperEmailAddress>
            <browser>
              <acceptHeader>text/html</acceptHeader>
              <userAgentHeader>Mozilla/5.0 ...</userAgentHeader>
            </browser>
          </shopper>
          <shippingAddress>
            <address>
              <firstName>A</firstName>
              <lastName>Shopper</lastName>
              <address1>47A</address1>
              <address2>Queensbridge Road</address2>
              <address3>Suburbia</address3>
              <postalCode>CB94BQ</postalCode>
              <city>Cambridge</city>
              <countryCode>GB</countryCode>
              <telephoneNumber></telephoneNumber>
            </address>
          </shippingAddress>
          <!-- Optional Risk Data -->
          <riskData>
            <authenticationRiskData authenticationMethod="localAccount">
              <authenticationTimestamp>
                <date second="01" minute="02" hour="03" dayOfMonth="01" month="06" year="2019"/>
              </authenticationTimestamp>
            </authenticationRiskData>
            <shopperAccountRiskData
              transactionsAttemptedLastDay="1"
              transactionsAttemptedLastYear="100"
              purchasesCompletedLastSixMonths="50"
              addCardAttemptsLastDay="1"
              previousSuspiciousActivity="true"
              shippingNameMatchesAccountName="true"
              shopperAccountAgeIndicator="lessThanThirtyDays"
              shopperAccountChangeIndicator="lessThanThirtyDays"
              shopperAccountPasswordChangeIndicator="noChange"
              shopperAccountShippingAddressUsageIndicator="thisTransaction"
              shopperAccountPaymentAccountIndicator="lessThanThirtyDays">          <shopperAccountCreationDate>
                <date dayOfMonth="01" month="02" year="2003"/>
              </shopperAccountCreationDate>
              <shopperAccountModificationDate>
                <date dayOfMonth="02" month="03" year="2004"/>
              </shopperAccountModificationDate>
              <shopperAccountPasswordChangeDate>
                <date dayOfMonth="03" month="04" year="2005"/>
              </shopperAccountPasswordChangeDate>
              <shopperAccountShippingAddressFirstUseDate>
                <date dayOfMonth="04" month="05" year="2006"/>
              </shopperAccountShippingAddressFirstUseDate>
              <shopperAccountPaymentAccountFirstUseDate>
                <date dayOfMonth="05" month="06" year="2007"/>
              </shopperAccountPaymentAccountFirstUseDate>
            </shopperAccountRiskData>
            <transactionRiskData
              shippingMethod="shipToBillingAddress"
              deliveryTimeframe="overnightShipping"
              deliveryEmailAddress="sp@worldpay.com"
              reorderingPreviousPurchases="true"
              preOrderPurchase="false"
              giftCardCount="1">
              <transactionRiskDataGiftCardAmount>
                <amount value="1" currencyCode="EUR" exponent="2"/>
              </transactionRiskDataGiftCardAmount>
              <transactionRiskDataPreOrderDate>
                <date dayOfMonth="06" month="07" year="2008"/>
              </transactionRiskDataPreOrderDate>
            </transactionRiskData>
          </riskData>
          <!-- mandatory additional3DSData -->
          <additional3DSData dfReferenceId="0_76607b65-03a2-45da-bf3c-858b1ea54f2c" challengeWindowSize="250x400" challengePreference="challengeRequested"/>
        </order>
      </submit>
    </paymentService>

    First request table

    ElementMandatory/OptionalDescription
    <riskData>OCustomer data from their account with you that will support your request
    <additional3DSData>MYou must provide the dfReferenceId you receive for DDC
    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN""http://dtd.worldpay.com/paymentService_v1.dtd"&gt;
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
      <reply>
        <orderStatus orderCode='YOUR_ORDER_CODE'>
          <challengeRequired>
            <threeDSChallengeDetails>
              <threeDSVersion>1.0.2</threeDSVersion>
              <transactionId3DS>UYt1Ey7Tjytf0vhP2jQ0</transactionId3DS>
              <acsURL>
                <![CDATA[https://www.clicksafe.lloydstsb.com/lloyds/tdsecure/opt_in_dispatcher.jsp?partner=debit&VAA=B]]&gt;
              </acsURL>
              <payload>eNpVUttuwjAMfc9XVPuAJm1KC8hE4jJtTGLqGIVtb1XxoBNNIU1h/fslBcaWJx/bcc45MSy2CnHyilmtUMAMqyrdoJOvB3fJu/bum2jx1ehPdtzG/tcLuxMQD+d4EHBEVeWlFJ7LXB/oFRIzQmXbVGoBaXYYTZ9F0PPCKAJ6gQQKVNOJ8Pwu67Ggw84H6DlNQKYFilXsjB+cqdSoZLpzFlhpZ5hlZQ20rRMwsdSqEV0/BHoFBGq1E1ut931KT6eTuyrVbr1Pm1iVx1xu3KwsgNoeAvTGNK5tVBkDvvO1WC53bJYsO8/e/IBv82L2+FEmk6dDshgOgNoOAutUo/CZz1iXhY7X6XPe5wHQNk8gLSwhMU9GTsRd3jHqzxkCe/vW8Iwibkt/M0ZXrRTKrBFhwI2wKyKA3/tSoukxdv/GRsaN/PjRmp5pY2MQsl43iPxewHjkcWbtbwvtnNxY5Rv27aC89Y3ay/TyufSyByb6tx8/MJ+y2Q==</payload>
            </threeDSChallengeDetails>
          </challengeRequired>
        </orderStatus>
      </reply>
    </paymentService>
    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN""http://dtd.worldpay.com/paymentService_v1.dtd"&gt;
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
      <submit>
        <order orderCode="YOUR_ORDER_CODE">
          <info3DSecure>
            <completedAuthentication/>
          </info3DSecure>
          <session id="ssn42abcd023">
        </order>
      </submit>
    </paymentService>
    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN""http://dtd.worldpay.com/paymentService_v1.dtd"&gt;
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
      <reply>
        <orderStatus orderCode="YOUR_ORDER_CODE">
          <payment>
            <paymentMethod>VISA_DEBIT-SSL</paymentMethod>
            <amount value="7335" currencyCode="EUR" exponent="2" debitCreditIndicator="credit"/>
            <lastEvent>AUTHORISED</lastEvent>
            <balance accountType="IN_PROCESS_AUTHORISED">
              <amount value="7335" currencyCode="EUR" exponent="2" debitCreditIndicator="credit"/>
            </balance>
            <cardNumber>4444**1111</cardNumber>
            <riskScore value="16"/>
          </payment>
        </orderStatus>
      </reply>
    </paymentService>

    Google Pay EMVCO_TOKEN example

    The following values are returned when the payment is SCA compliant and does not need to go through the 3DS process.

    If the cryptogram is not presented, then 3DS is required.

    • <cryptogram>BLIYQ4EA/wLF4/hK9ABFMAABAAA=</cryptogram>
    • <eciIndicator>07</eciIndicator>
    Copied!
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN" "http://dtd.worldpay.com/paymentService_v1.dtd"&gt;
    <paymentService version="1.4" merchantCode="YOUR_MERCHANT_CODE">
      <submit>
        <order orderCode="YOUR_ORDER_CODE">
          <description>test order</description>
          <amount value="100" currencyCode="EUR" exponent="2"/>
          <orderContent>
            <![CDATA[]]>
          </orderContent>
          <paymentDetails>
          <!--Choose type: APPLEPAY|SAMSUNGPAY|GOOGLEPAY|NETWORKTOKEN-->
            <EMVCO_TOKEN-SSL type="GOOGLE_PAY">
              <tokenNumber>4444333322221111</tokenNumber>
              <expiryDate>
                <date month="12" year="2021"/>
              </expiryDate>
              <cardHolderName>A Shopper</cardHolderName>
              <cryptogram>BLIYQ4EA/wLF4/hK9ABFMAABAAA=</cryptogram>
              <eciIndicator>07</eciIndicator>

    Testing

    1. Create a JWT with static values

    Below are 3 static values to test theJWT creation. All other claims must be present and not empty but can otherwise take any value.

    AttributeValue
    OrgUnitId5bd9b55e4444761ac0af1c80
    iss5bd9e0e4444dce153428c940
    JWT MAC Keyfa2daee2-1fbb-45ff-4444-52805d5cd9e0 (pass this as a string and not a number)

    2. DDC test URLs

    Run the Javascript tosubmit the Device Databack to the URL of the Worldpay simulator:

    https://secure-test.worldpay.com/shopper/3ds/ddc.html

    3. Create the initial XML request

    Use the below magic values and test card numbers to create your initial XML.

    Test card numbers

    Insert the below values in <cardNumber>:

    Card typeTest card number
    American Express343434343434343
    Diners36700102000000 and 36148900647913
    JCB3528000700000000
    MasterCard5555555555554444, 5454545454545454 and 2221000000000009
    MasterCard Debit5163613613613613
    Visa4444333322221111, 4911830000000 and 4917610000000000
    Visa Debit4462030000000000 and 4917610000000000003
    Visa Electron (UK only)4917300800000000
    Visa Purchasing4484070000000000

    Magic values

    Insert the below values in <cardHolderName> to simulate the payment outcome described in column 4. For Google Pay, insert the value in <description>:

    Magic valueScenario simulatedAuthentication OutcomePayment Outcome
    3DS_V1_CHALLENGE_IDENTIFIEDSuccessful challengeCardholder authenticatedAUTHORISED
    3DS_V1_CHALLENGE_NOT_IDENTIFIEDSuccessful challengeAuthentication offered but not usedAUTHORISED
    3DS_V1_CHALLENGE_VALID_ERRORChallenge requiredAuthentication unavailableAUTHORISED
    3DS_V1_NOT_ENROLLEDCard not enrolled in 3DS1Authentication offered but not used (Non-Amex)
    None (Amex)
    AUTHORISED
    3DS_V1_UNKNOWN_ENROLMENT3DS1 enrolment status unavailable (external/internal error)Authentication UnavailableAUTHORISED
    3DS_V1_CHALLENGE_UNKNOWN_IDENTITYChallenge failedAuthentication attempted but failedREFUSED
    3DS_V2_FRICTIONLESS_IDENTIFIEDFrictionless authenticationCardholder authenticatedAUTHORISED
    3DS_V2_FRICTIONLESS_NOT_IDENTIFIEDFrictionless attempted (offered but not used)Authentication offered but not usedAUTHORISED
    3DS_V2_FRICTIONLESS_REJECTEDFrictionless rejectedAuthentication rejected.Do not attempt authorisation.REFUSED
    3DS_V2_FRICTIONLESS_VALID_ERROR3DS2 frictionless authentication unavailable (external/internal error)Authentication UnavailableAUTHORISED
    3DS_V2_CHALLENGE_IDENTIFIEDSuccessful challengeCardholder authenticatedAUTHORISED
    3DS_V2_CHALLENGE_VALID_ERRORChallenge requiredAuthentication unavailableAUTHORISED
    3DS_V2_CHALLENGE_UNKNOWN_IDENTITYChallenge failedAuthentication attempted but failedREFUSED
    3DS_BYPASSED3DS bypassednoneAUTHORISED
    3DS_V2_BYPASSED_AFTER_CHALLENGEChallenge requirednoneAUTHORISED

    Note: Ensure you are sending the dfReferenceId in yourinitial XML. Extract the value for this from the SessionId that is returned by the Worldpay Device Data Collection simulator.

    4. Challenge

    If you have decided to use a magic value that invokes achallenge(3DS_V1_CHALLENGE_IDENTIFIED or 3DS_V2_CHALLENGE_IDENTIFIED) you must create another JWT. Use the same static values as inStep 1.

    Use the below URL to POST your challenge JWT in an iframe:

    https://secure-test.worldpay.com/shopper/3ds/challenge.html

    The Worldpay simulator POSTs back a standard response to the iframe. You are presented with an OK button. Click this to be redirected to your ReturnUrl.

    5. Second XML request

    Submit yoursecond XML request, making sure to include the same orderCode and session id you used in the initial XML request.

    You receive a Worldpay response with the payment outcome.

    3DS 2.2

    3DS Flex supports EMV 3DS 2.2 in line with the market.

    Exemptions in Authentication

    Exemptions in authentication can only be requested where all parties support 3DS 2.2 (or later), or 2.1+ for Mastercard.

    Follow the link below for more information about Worldpay’s exemption services.https://beta.developer.worldpay.com/docs/wpg/scaexemptionservices/exemptionengine

    About this guide

    To see the latest changes made to this guide please clickhere.