3DSv2 Sagepay Direct Integration basics

拈花ヽ惹草 提交于 2019-12-02 06:43:31

Check that the PaRes you send to SagePay has no spaces. I found that some PaRes I got back had spaces. I replace spaces (" ") with a + sign like this:

PaRes = PaRes.Replace(" ", "+")

Then I use the altered value in the URL.

You are talking about 3DSecure version 1, right? 3DSecure version 2 looks for cres (cres in small letters).

Hope this helps.

I think it's v1 as that is in the URLs. This is all new to me. I've seen no mention of V2 anywhere yet ?

There isn't any spaces in the PaRes returned anyway.

WRT "the deadline" mentioned, my understanding of the timeline is as follows:

  1. On 14th September 2019, all ecommerce transactions need to go through some form of 3-D Secure. So integrating Direct API v3.00 or v4.00 with 3-D Secure would be fine (consumers will get a better experience with v4.00). SagePay also switch on their v4.00 API around this time, which I sort of anticipate might have teething problems.
  2. Some point in 2020: All transactions should go through 3-D Secure V2 (i.e Direct API v4.00), as card schemes (i.e Visa / MasterCard) will stop supporting 3-D Secure V1. So all merchants that have a Direct integration with SagePay for ecommerce payments will have to upgrade to v4.00

Smitthhy - To test the flow on the SagePay test system where the user has to do something, you have to send through a CardHolder of CHALLENGE (I can't reply to the other post because I don't have a SO reputation of 50 yet - sigh).

Well written Smitthhy. I do agree that the documentation could be more straight forward. Did my head in the past weeks. To your 1) you need to post all required fields mentioned in the documentation. To 8) you get more data back from SagePay. Looks like below:

VPSProtocol=4.00
Status=OK
StatusDetail=0000 : The Authorisation was Successful.
VPSTxId={1B19CB3F-E553-0E69-CFD5-6D75B53753C1}
SecurityKey=UAW4ZETUK7
TxAuthNo=2261559
AVSCV2=SECURITY CODE MATCH ONLY
AddressResult=NOTMATCHED
PostCodeResult=NOTMATCHED
CV2Result=MATCHED
3DSecureStatus=OK
CAVV=Q042ZUZRWndDbjAyWHRjYUFkZ2c=
DeclineCode=00
ExpiryDate=1035
BankAuthCode=999778

To 9) You can differentiate between 3DSecure v1 and 3DSecure v2 by checking the StatusDetails you get from SagePay.

3DSv1 returns StatusDetail=2007
3DSv2 returns StatusDetail=2021

I do the Payment Processing in a class file which is called by my Checkout page. The class returns the response to the Checkout page with the details I need. They are different for v1 and v2.

For v1 I return:
return "v1" + Status + "&3DSecureStatus=" + s3DSecureStatus + "&MD=" + sMD + "&ACSURL=" + sACSURL + "&PAReq=" + sPAReq + "&VendorTxCode=" + o.OrderID;

For v2 I return:
return "v2" + Status + "&3DSecureStatus=" + s3DSecureStatus + "&VPSTxId=" + sVPSTxId + "&ACSURL=" + sACSURL + "&CReq=" + sCReq + "&VendorTxCode=" + o.OrderID;

This allows me to act accordingly in the Checkout code.

I redirect v1 to a page with the iFrame. The iFrame loads the ACSURL received in the reponse earlier. This shows straight away the "challenge" window.

This is mainly down to the fact that SagePay accepts a URL with a query string for 3DSv1 and expects a form post for 3DSv2.

v2 redirects to another page with an iFrame. The iFrame loads a page on my website first:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ChallengeiFrame.aspx.cs" Inherits="ac_ChallengeiFrame" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
     <title></title>
</head>
<body>
 <div id="content">
 <div id="contentHeader">
    Your Bank requires Authentication
 </div>
 <p>
     Please click the button below to continue.
 </p>

 <form action="<%= sACSUrl %>" method="post">
     <input type="hidden" name="creq" value="<%= sCReq %>" />
     <input type="hidden" name="threeDSSessionData" value="<%= sVPSTxId %>" />
     <input type="submit" value="Click to continue" />
 </form>
 </div>
 </body>
 </html>

Clicking the button posts to the ACSURL and the challenge window shows. The customer fills in the password and submits. The bank responds to the page you assigned for v1 it's the TermUrl and for v2 it's the ThreeDSNotificationURL.

On those pages you post what you need to post and handle the response from SagePay. If all goes well the payment has been received and you can redirect your customer to the Thank you page and finish the order.

Basically, I use 2 sets of code files to handle 3DSv1 and 3DSv2. Like to keep it seperated and once 3DSv1 gets discontinued I simply can delete those files and remove the code blocks in my payment processing page and checkout page. Shouldbe straight forward then.

Hope this helps.

Here the link to the 3DSecure v2 documentation: DIRECT_Integration_and_Protocol_4_Guidelines.pdf

EDIT

ThreeDSNotificationURL code for the WebRequest and HttpWebResponse:

   /////////////////////////////////////////////////////////
   //// This is to get the posted results from the bank
   /////////////////////////////////////////////////////////
   NameValueCollection coll;
   coll = Request.Form;
   /////////////////////////////////////////////////////////
   string sSagePayUrl = "";
   if (EcommerceSettings.bLiveTransactions()) //That's just some logic so I can control live or test at one place in a settings class file.
   {
        sSagePayUrl = "https://live.sagepay.com/gateway/service/direct3dcallback.vsp?";
   }
   else
   {
        sSagePayUrl = "https://test.sagepay.com/gateway/service/direct3dcallback.vsp?";
   }
   ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
   WebRequest request = WebRequest.Create(sSagePayUrl + "cres=" + coll["cres"] + "&VPSTxId=" + coll["threeDSSessionData"]);
   // Get the response.
   HttpWebResponse getResponse = (HttpWebResponse)request.GetResponse();
   // Display the status.
   // Get the stream containing content returned by the server.
   Stream dataStream = getResponse.GetResponseStream();
   // Open the stream using a StreamReader for easy access.
   StreamReader reader = new StreamReader(dataStream);
   // Read the content.
   string responseFromServer = reader.ReadToEnd();
   // Cleanup the streams and the response.
   reader.Close();
   dataStream.Close();
   getResponse.Close();
   //Check the response and act accordingly

You need to build the request differently for 3DSv1: Here my way in the TermUrl page:

vendorTxCode = Request.QueryString["VendorTx"];
NameValueCollection coll;
coll = Request.Form;
sMd = coll["MD"];
sPaRes = coll["PaRes"];

////////////////////////////////////////////////////
//// Build post to SagePay
////////////////////////////////////////////////////
StringBuilder sb = new StringBuilder();
if (EcommerceSettings.bLiveTransactions())
{
    sb.Append("https://live.sagepay.com/gateway/service/direct3dcallback.vsp?");
}
else
{
    sb.Append("https://test.sagepay.com/gateway/service/direct3dcallback.vsp?");
}
sb.Append("VendorTxCode=");
sb.Append(sOrderID);
sb.Append("&MD=");
sb.Append(sMD);
sb.Append("&PaRes=");
sPaRes = sPaRes.Replace(" ", "+");//HttpUtility.UrlEncode(sPaRes);
sb.Append(sPaRes);
string sRequestQuery = sb.ToString();

/////////////////////////////////////////////////////
//// Post To SagePay 3DCallback page
/////////////////////////////////////////////////////
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
WebRequest request = WebRequest.Create(sRequestQuery);

Handle the response as shown for v2.

EDIT

I found that you best do a post for 3DSv1 and not using the URL parameters as some banks do not accept this. Pretty much the same way as for 3DSv2

This never came up on the test server and happened only when live.

It looks like the test server is not providing the various possibilities we encounter once we are live.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!