Menu

Making a 3DS Payment From Your Server

This guide describes how to place an order using 3D Secure.


For information about which payment options you can combine with a 3D Secure payment, please checkhere.

To make a 3DS payment, you will first to obtain your your customer's card details using one of the methods above.

Making a 3DS payment is then a 3-step process:

  • Initiate the payment from your server
  • Redirect the customer to the 3DS page
  • Send the 3DS response back to Worldpay and get final order confirmation

1. Starting a 3D Secure Order - initiate the payment from your server

To make a 3DS payment, you must obtain your customer's card details using one of the methods above.

When sending the order request, make sure you include the additional 3DS fields, and, for test orders, set the shopper name to the special value 3D. Details can be found in theAPI Reference, or you can check the code examples below:

Copied!
curl https://api.worldpay.com/v1/orders
-H "Authorization: your-test-service-key"
-H "Content-type: application/json"
-X POST
-d '{
        "token": "your-order-token",
        "orderType": "ECOM",
        "orderDescription": "Goods and Services",
        "amount": 10917,
        "currencyCode": "GBP",
        "name": "3D",
        "billingAddress": {
            "address1": "18 Linver Road", 
            "postalCode": "SW6 3RB", 
            "city": "London", 
            "countryCode": "GB"
        },
        "customerIdentifiers": {
                "email": "john.smith@gmail.com"
        },
        "is3DSOrder": true,
        "shopperAcceptHeader": "acceptheader",
        "shopperUserAgent": "user agent 1",
        "shopperSessionId": "123",
        "shopperIpAddress": "195.35.90.111"
}'

The response to this call will include the following JSON object:

{
        "orderCode": "worldpay-generated-order-code",
        "token": "your-order-token",
        "orderDescription": "Goods and Services",
        "is3DSOrder": true,
        "amount": 10917,
        "currencyCode": "GBP",
        "paymentStatus": "PRE_AUTHORIZED",
        "paymentResponse": {
                "type": "ObfuscatedCard",
                "name": "3D",
                "expiryMonth": 2,
                "expiryYear": 2016,
                "issueNumber": 1,
                "startMonth": 1,
                "startYear": 2013,
                "cardType": "VISA",
                "maskedCardNumber": "**** **** **** 1111",
                "billingAddress": {
                        "address1": "18 Linver Road",
                        "address2": "",
                        "address3": "",
                        "postalCode": "SW6 3RB",
                        "city": "London",
                        "state": "",
                        "countryCode": "GB"
                }
        },
        "redirectURL": "https://www.card-issuer.com/3DSecureAuthentication",
        "oneTime3DsToken": "somebase64encodeddata",
        "customerIdentifiers": {
                "email": "john.smith@gmail.com"
        },
        "environment": "TEST"
}
$worldpay = new Worldpay('your-test-service-key');
$billing_address = array(
    "address1"=>'123 House Road',
    "address2"=> 'A village',
    "address3"=> '',
    "postalCode"=> 'EC1 1AA',
    "city"=> 'London',
    "state"=> '',
    "countryCode"=> 'GB',
);
try {
    $response = $worldpay->createOrder(array(
        'token' => 'your-order-token',
        'amount-in-cents' => 500,
        'currencyCode' => 'GBP',
        'name' => '3D',
        'billingAddress' => $billing_address,
        'orderDescription' => 'Order description',
        'customerOrderCode' => 'Order code',
        'is3DSOrder' => true
    ));
    $_SESSION['orderCode'] = $response['orderCode'];
    $oneTime3DsToken = $response['oneTime3DsToken'];
    $redirectURL = $response['redirectURL'];
} catch (WorldpayException $e) {
    echo 'Error code: ' .$e->getCustomCode() .'

    HTTP status code:' . $e->getHttpStatusCode() . '

    Error description: ' . $e->getDescription()  . '

    Error message: ' . $e->getMessage();
}
worldpay = Worldpay.new('your-test-service-key')

begin

    billingAddress = {
        "address1"=>'123 House Road',
        "address2"=> 'A village',
        "address3"=> '',
        "postalCode"=> 'EC1 1AA',
        "city"=> 'London',
        "state"=> '',
        "countryCode"=> 'GB'
    }
    response = worldpay.createOrder({
        'token' => 'your-order-token',
        'amount' => 500,
        'currencyCode' => 'GBP',
        'name' => '3D',
        'billingAddress' => billingAddress,
        'orderDescription' => 'Order description',
        'customerOrderCode' => 'Order code',
        '3DS' => true,
        'shopperIpAddress' => request.remote_ip,
        'shopperSessionId' => request.session_options[:id],
        'shopperUserAgent' => request.env['HTTP_USER_AGENT'],
        'shopperAcceptHeader' => request.env['HTTP_ACCEPT']
    })
    redirectURL = response['body']['redirectURL']
    oneTime3DsToken = response['body']['oneTime3DsToken']

rescue Exception => e
    print e.to_s
end
WorldpayRestClient restClient = new WorldpayRestClient("https://api.worldpay.com/v1", "your-test-service-key");

var address = new Address()
{
    address1 = "123 House Road",
    address2 = "A village",
    city = "London",
    countryCode = CountryCode.GB,
    postalCode = "EC1 1AA"
};

var orderRequest = new OrderRequest()
{
    token = "your-token",
    amount = 500,
    currencyCode = CurrencyCode.GBP,
    name = "test name",
    billingAddress = address,
    orderDescription = "Order description",
    customerOrderCode = "Order code",
    is3DSOrder = true
};

try
{
    OrderResponse orderResponse = restClient.GetOrderService().Create(orderRequest);

    string orderCode;
    string oneTime3DsToken;
    string redirectURL;
    if (orderResponse.paymentStatus == OrderStatus.PRE_AUTHORIZED)
    {
        orderCode = orderResponse.orderCode;
        oneTime3DsToken = orderResponse.oneTime3DsToken;
        redirectURL = orderResponse.redirectURL;
    }
    else
    {
        throw new WorldpayException("Expected order status PRE_AUTHORIZED");
    }
}
catch (WorldpayException e)
{
    Console.WriteLine("Error code:" + e.apiError.customCode);
    Console.WriteLine("Error description: " + e.apiError.description);
    Console.WriteLine("Error message: " + e.apiError.message);
}

Note the payment status PRE_AUTHORIZED: this shows that the first step of the 3DS order is complete. Your shopper must now complete authentication at the redirect URL, as supplied by the card issuer in the response.

If the card issuer does not support 3D Secure then you will receive the normal non-3DS order response - without a redirection URL - and the following steps will not be required.

2. Redirect the customer to the 3DS page - secure authentication

The shopper must be redirected to the URL of the issuer's 3DS authentication site given in the redirect URL element above, by submitting an HTTP POST. After they have completed authentication, they will then typically be redirected back to your site.

The HTTP POST you submit must:

  • Contain the PaReq attribute. Please use the value of the oneTime3DsToken element of the reply message above.
  • Contain the TermUrl attribute. Use a URL where you want the shopper to be redirected after authentication on the issuer’s 3DS site, which will use a POST. This will typically be a "Successful Order!" page on your site.

You can use the optional Merchant Data MD attribute to supply any identifying information you need in the return POST.

Provided that the shopper has enabled JavaScript in the browser, they will automatically be forwarded to their bank’s website. If JavaScript has been disabled, the shopper must press the "submit" button to continue.

The following example HTML page redirects the shopper to the issuer’s 3DS authentication site:

Copied!
<html>
   <head>
      <title>3-D Secure helper page</title>
   </head>
   <body OnLoad="OnLoadEvent();">
      This page should forward you to your own card issuer for 
      identification. If your browser does not start loading the 
      page, press the Submit button. <br/>
      After you successfully identify yourself you will be sent back 
      to this website, where the payment process will continue.<br/>
      <form name="theForm" method="POST" action="value of the redirectURL element">
         <input type="hidden" name="PaReq" value="value of the oneTime3DsToken element" /> 
         <input type="hidden" name="TermUrl" value="URL you want the shopper to return to after 3D Secure authentication" />
         <input type="hidden" name="MD" value="Merchant supplied data" />
         <input type="submit" name="Identify yourself" />
      </form>
      <script language="Javascript">
         <!--
                 function OnLoadEvent()
                 {
                         // Make the form post as soon as it has been loaded.
                         document.theForm.submit();
                 }
         // -->
      </script>
   </body>
</html>

Here's an example of how it would look as a bare http POST:

Copied!
curl https://www.mybank.co.uk/3dsecure
-H "Content-type: application/x-www-form-urlencoded"
-X POST
-d 'TermUrl=https://www.yourmerchantsite.co.uk/3DSecureConfirmation&PaReq=value-of-oneTime3DsToken&MD=merchantdatavalue'

Here is an example POST from the issuer site back to the TermUrl after authentication:

Copied!
<html>
   <head>
      <title>Example Issuer Redirection to Merchant Site</title>
   </head>
   <body OnLoad="OnLoadEvent();">
      3D Secure authentication is complete. Please wait as you are 
      being redirected to the merchant's website.If this does not
      happen shortly, please press the button you see.
      <br/>
      <form name="theForm" method="POST" 
         action="https://www.yourmerchantsite.co.uk/3DSecureConfirmation"&gt;
         <input type="hidden" name="MD" value="merchantdatavalue"/>
         <input type="hidden" name="PaRes" value="3dsecure-response-data"/>
         <input type="submit" name="Continue"/>
      </form>
      <script language="Javascript">
         <!--
         function OnLoadEvent() {
         // Make the form post as soon as it has been loaded.
         document.theForm.submit();
         }
         // -->
      </script>
   </body>
</html>

3. Send the 3DS response back to Worldpay and get final order confirmation

The order will be updated with the 3DS response once you have sent Worldpay a second order request. This must include the 3DS response code PaRes you received from the issuer/shopper’s bank at the end of the previous step:

Copied!
curl https://api.worldpay.com/v1/orders/worldpay-generated-order-code
-H "Authorization: your-test-service-key"
-H "Content-type: application/json"
-X PUT
-d '{
        "threeDSResponseCode": "value of PaRes",
        "shopperAcceptHeader": "acceptheader",
        "shopperUserAgent": "user agent 1",
        "shopperSessionId": "123",
        "shopperIpAddress": "195.35.90.111"
}'

The response to this call will include the following JSON object:

{
        "orderCode": "worldpay-generated-order-code",
        "token": "your-order-token",
        "orderDescription": "Goods and Services",
        "is3DSOrder": true,
        "amount": 10917,
        "currencyCode": "GBP",
        "paymentStatus": "SUCCESS",
        "paymentResponse": {
                "type": "ObfuscatedCard",
                "name": "3D",
                "expiryMonth": 2,
                "expiryYear": 2016,
                "issueNumber": 1,
                "startMonth": 1,
                "startYear": 2013,
                "cardType": "VISA",
                "maskedCardNumber": "**** **** **** 1111",
                "billingAddress": {
                        "address1": "18 Linver Road",
                        "address2": null,
                        "address3": null,
                        "postalCode": "SW6 3RB",
                        "city": "London",
                        "state": null,
                        "countryCode": "GB"
                }
        },
        "customerIdentifiers": {
                "email": "john.smith@gmail.com"
        },
        "environment": "TEST"
}
$worldpay = new Worldpay('your-test-service-key');
try {
    $response = $worldpay->authorise3DSOrder($_SESSION['orderCode'], $_POST['PaRes']);
    if (isset($response['paymentStatus']) && $response['paymentStatus'] == 'SUCCESS') {
        echo 'Order Code: ' . $_SESSION['orderCode'] . ' has been authorised ';
    } else {
        echo 'There was a problem authorising 3DS order ';
    }
} catch (WorldpayException $e) {
    echo 'Error code: ' .$e->getCustomCode() .'
    HTTP status code:' . $e->getHttpStatusCode() . '
    Error description: ' . $e->getDescription()  . '
    Error message: ' . $e->getMessage();
}
worldpay = Worldpay.new('your-test-service-key')
begin
    threeDsInfo = {
        'shopperIpAddress' => request.remote_ip,
        'shopperSessionId' => request.session_options[:id],
        'shopperUserAgent' => request.env['HTTP_USER_AGENT'],
        'shopperAcceptHeader' => request.env['HTTP_ACCEPT']
    }
    orderCode = session[:orderCode]
    responseCode = request.POST['PaRes']
    response = worldpay.authorise3DSOrder(orderCode, responseCode, threeDsInfo)
    @_worldpayOrderCode = response['body']['orderCode']
rescue Exception => e
    print e.to_s
end

In the example above, the order has a SUCCESS status. In the case of an authorisation-only order, the status would be AUTHORIZED. If the shopper failed authentication or there was another error, the status would be FAILED.

You have now successfully created your first 3DS order!

Suggested next steps: