Paypal Payflow透明重定向,使用AJAX的SecureToken?

Ric*_*eMN 7 ajax paypal asp.net-ajax token payflowpro

我正在使用Payflow Pro(https://pilot-payflowpro.paypal.com)尝试成为PCI兼容的C#VS2012 Framework 4.5 MVC应用程序.我们多年来一直在使用PayflowPro,这就是我必须使用的.从我的阅读中看来,我似乎应该使用透明重定向,所以我不会向我的网络服务器发布任何私有内容,但我不知道我是否需要这个以及我希望如何处理这个问题.我也有几个问题......

我怎么认为这一切都有效: 我的理解是你需要一个安全的语言(与Paypal的通信,旅程1).然后您发布安全数据(CC,exp,安全代码),包括securetoken(与Paypal的通信,旅程2)并接收销售的授权和交易ID.

我希望如何做到这一点: 我打算拥有一个包含所有信息(用户详细信息,运送详细信息和CC信息)的表单,当用户按下购买按钮时,我将使用AJAX处理1到我的服务器(没有发送安全的用户信息).在这里,我将创建URL + params并发送paypal我的un/pw信息以检索令牌(全部来自我的服务器).响应将返回给客户端,如果成功,我将直接通过AJAX与Paypal的网关服务器进行通信,这次发送安全的CC信息+令牌(行程#2).根据对#2行程的响应,我会让用户知道他们的购买行为是什么.旅行2不应该需要我的Paypal UN/PW信息,因为它可以很容易地在客户端看到,我包括应该识别原始交易的SecureToken.根据我的解释,我认为不需要透明重定向.或者我在这里遗漏了什么?

另外,我想使用什么交易类型?为行程#1创建"授权",然后为行程#2创建"销售"?

所以这里是细节编码类型的东西: 对于我的研发测试,我正在建立自己的名称/值对参数字符串(见下文),并通过WebRequest通过他们的沙箱/测试网址(pilot-payflowpro.paypal)与网关服务器进行通信. COM).我得到了一个成功的回复和SECURETOKEN回来了.安全令牌的初始请求(如下所示)为TRXTYPE = A(授权),不发送卡信息.我想先授权吗?

以下是我的参数(也可能包括shipto信息,但未在下面列出):

USER=myAuthUserName
&VENDOR=myAuthUserName
&PARTNER=myPartner
&PWD=myPassword
&AMT=21.43
&BILLTOFIRSTNAME=FName
&BILLTOLASTNAME=LName
&BILLTOSTREET=123 Main Street
&BILLTOSTREET2=Apt 203B
&BILLTOCITY=MyCity
&BILLTOSTATE=CA
&BILLTOZIP=77777
&BILLTOPHONENUM=4444444444
&EMAIL=myemail@somedomain.com
&CURRENCY=USD
**&TRXTYPE=A**
&SILENTTRAN=TRUE
&CREATESECURETOKEN=Y
&SECURETOKENID=a99998afe2474b1b82c8214c0824df99
Run Code Online (Sandbox Code Playgroud)

正如我所说,我得到了一个成功的响应,并转到下一步发送安全数据(CC#,EXPDATE,安全代码).当我从params中删除我的UN/PW/VENDOR /合作伙伴信息时,由于无效的用户身份验证而出现错误.但是,看到我正在动态建立第二个电话,我不能在那里使用我的PayPal/pw.我错过了什么?有人提供上述此问题或其他问题的帮助吗?

如果我需要添加任何说明,请告诉我.在此先感谢您的时间!

Ric*_*eMN 7

在花了很多时间与Paypal工程师合作后,我成功找到了Paypal的Payflow透明重定向解决方案,没有托管页面(有自己的支付页面).同样,这里的文档对于工程师而言非常混乱: Payflow API文档.此外,代码没有优化,因为它只是一个研发应用程序,但总的来说,它对我有用.只是一个例子和解释,我相信有更好的方法来做个别的步骤.希望这会有所帮助,并允许您绕过一些减慢Paypal Payflow集成速度的障碍.

是的,它符合PCI标准,因为没有安全的客户数据会打到您自己的服务器上.请记住,PCI合规性非常复杂且涉及,但这是其中很重要的一部分.好的,所以我将解释我在MVC C#环境中做了哪些工作.我将解释这里的步骤,然后包括下面的代码.

  1. 客户:客户端完成向购物车添加商品并按下购买按钮.Javascript处理按钮单击,不提交,并带您进入下一步.
  2. 客户端 - >服务器:AJAX函数POSTS到服务器方法联系Paypal获取一次性安全令牌.此通信使用您的身份验证,唯一的交易ID(guid)以及有关交易的非安全详细信息(总计,账单信息,送货信息,返回URL详细信息)将您(商家)识别为paypal.这样,您的所有商家个人信息都是安全的(Web服务器到Paypal).
  3. 服务器 - >客户端:从上面的事务中,您将收到一个包含安全令牌的参数字符串(除其他内容外,请参见示例方法).使用这条信息,我动态创建我最终需要在客户端上用于透明重定向部分的URL,并将url字符串发送回客户端.
  4. 客户端:使用步骤3中返回的URL,我通过使用jQuery添加所需的信用卡参数来完成URL.
  5. 客户 - > PAYPAL:这是我不明白该做什么的地方.虽然步骤#2是一个帖子,但这一步骤将是一个REDIRECT.当然,这看起来很合适,因为它被称为"透明重定向",但这部分对我来说没有意义.因此,一旦您的整个网址完成,您就会将窗口重定向到Paypal以处理您的交易.
  6. PAYPAL - > SERVER:PayPal回发到您在步骤2中包含的其中一个URL(到我的一个控制器上的公共方法),然后我读取响应对象并解析参数.

容易,对吗?或许,但对我而言,第5步给我带来了很大的问题.我正在使用POST,但不明白为什么我一直在回复错误.这是一个关于无效商家或身份验证的HTML页面.请记住重定向,而不是发布第5步.

代码:

第1步:在按钮上单击onclick属性以调用GetToken函数.

第2步和第3步:

客户端:

function GetToken() {
$.ajax({
    url: '@Url.Action("GetToken", "MyController")',
    type: 'POST',
    cache: 'false',
    contentType: 'application/json; charset=utf-8',
    dataType: 'text',
    success: function (data) {
        // data is already formatted in parameter string
        SendCCDetailsToPaypal(data);
    },
    //error: 
    //TODO Handle the BAD stuff 
});}
Run Code Online (Sandbox Code Playgroud)

服务器端:

我有单独的方法用于构建令牌请求所需的所有参数值.前三个构建:身份验证,事务详细信息,透明重定向.我在web.config文件中保留urls和payflow acct信息.最后一个方法ProcessTokenTransaction通过WebRequest完成与Paypal联系的所有繁重工作,然后将其解析为将发送回客户端的URL.这种方法应该重构为更清洁的交付,但我会留给你.ParseResponse是一种填充我创建的简单模型并返回该模型的方法.

令牌(沙盒)的URL: https ://pilot-payflowpro.paypal.com

这与TOKEN URL不同!! 用于PaypalTranactionAPI配置值.

交易网址:(沙箱) https://pilot-payflowlink.paypal.com

private  string PrepareApiAuthenticationParams()        
    {
        var paypalUser = ConfigurationManager.AppSettings["PaypalUser"];
        var paypalVendor = ConfigurationManager.AppSettings["PaypalVendor"];
        var paypalPartner = ConfigurationManager.AppSettings["PaypalPartner"];
        var paypalPw = ConfigurationManager.AppSettings["PaypalPwd"];

        //var amount = (decimal)19.53;

        var apiParams = @"USER=" + paypalUser
                        + "&VENDOR=" + paypalVendor
                        + "&PARTNER=" + paypalPartner
                        + "&PWD=" + paypalPw
                        + "&TENDER=C"
                        + "&TRXTYPE=A"
                        + "&VERBOSITY=HIGH";

        // find more appropriate place for this param
        //+ "&VERBOSITY=HIGH";

        return apiParams;
    }


    private  string PrepareTransactionParams(CustomerDetail detail)
    {
        var currencyType = "USD";

        var transactionParams = @"&BILLTOFIRSTNAME=" + detail.FirstName
                                + "&BILLTOLASTNAME=" + detail.LastName
                                + "&BILLTOSTREET=" + detail.Address1
                                + "&BILLTOSTREET2=" + detail.Address2
                                + "&BILLTOCITY=" + detail.City
                                + "&BILLTOSTATE=" + detail.State
            //+ "&BILLTOCOUNTRY=" + detail.Country +  // NEEDS 3 digit country code
                                + "&BILLTOZIP=" + detail.Zip
                                + "&BILLTOPHONENUM=" + detail.PhoneNum
                                + "&EMAIL=" + detail.Email
                                + "&CURRENCY=" + currencyType
                                + "&AMT=" + GET_VALUE_FROM_DB
                                + "&ERRORURL= " + HostUrl + "/Checkout/Error"
                                + "&CANCELURL=" + HostUrl + "/Checkout/Cancel"
                                + "&RETURNURL=" + HostUrl + "/Checkout/Success";   

        // ADD SHIPTO info for address validation

        return transactionParams;
    }


private  string PrepareTransparentParams(string requestId, string transType)
    {
        var transparentParams = @"&TRXTYPE=" + transType +
                               "&SILENTTRAN=TRUE" +
                               "&CREATESECURETOKEN=Y" +
                               "&SECURETOKENID=" + requestId;

        return transparentParams;
    }


    // Method to build parameter string, and create webrequest object
public string ProcessTokenTransaction()
    {
        var result = "RESULT=0"; // default failure response
        var transactionType = "A";
        var secureToken = string.Empty;
        var requestId = Guid.NewGuid().ToString().Replace("-", string.Empty);

        var baseUrl = ConfigurationManager.AppSettings["PaypalGatewayAPI"];            

        var apiAuthenticationParams = PrepareApiAuthenticationParams();

        // Create url parameter name/value parameter string
        var apiTransactionParams = PrepareTransactionParams(detail);

        // PCI compliance, Create url parameter name/value parameter string specific to TRANSAPARENT PROCESSING 
        var transparentParams = PrepareTransparentParams(requestId, transactionType);

        var url = baseUrl;
        var parameters = apiAuthenticationParams + apiTransactionParams + transparentParams;


        // base api url + required 
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "text/name"; // Payflow?
        request.Headers.Add("X-VPS-REQUEST-ID", requestId);

        byte[] bytes = Encoding.UTF8.GetBytes(parameters);
        request.ContentLength = bytes.Length;

        Stream requestStream = request.GetRequestStream();
        requestStream.Write(bytes, 0, bytes.Length);
        requestStream.Close();


        WebResponse response = request.GetResponse();
        Stream stream = response.GetResponseStream();
        StreamReader reader = new StreamReader(stream);

        try
        {

            // sample successful response
            // RESULT=0&RESPMSG=Approved&SECURETOKEN=9pOyyUMAwRUWmmv9nMn7zhQ0h&SECURETOKENID=5e3c50a4c3d54ef8b412e358d24c8915

            result = reader.ReadToEnd();

            var token = ParseResponse(result, requestId, transactionType);

            var transactionUrl = ConfigurationManager.AppSettings["PaypalTransactionAPI"];
            secureToken = transactionUrl + "?SECURETOKEN=" + token.SecureToken + "&SECURETOKENID=" + requestId;

            //ameValueCollection parsedParams = HttpUtility.ParseQueryString(result);                

            stream.Dispose();
            reader.Dispose();
        }
        catch (WebException ex)
        {
            System.Diagnostics.Trace.WriteLine(ex.Message);

        }
        finally { request.Abort(); }

        return secureToken;
    }


private TokenResponse ParseResponse(string response, string requestId, string transactionType)
    {
        var nameValues = HttpUtility.ParseQueryString(response);

        int result = -999;  // invalid result to guarantee failure

        int.TryParse(nameValues.Get(TokenResponse.ResponseParameters.RESULT.ToString()), out result);

        // retrieving response message
        var responseMessage = nameValues.Get(TokenResponse.ResponseParameters.RESPMSG.ToString());

        // retrieving token value, if any
        var secureToken = nameValues.Get(TokenResponse.ResponseParameters.SECURETOKEN.ToString());

        var reference = nameValues.Get(TokenResponse.ResponseParameters.PNREF.ToString());

        var authCode = nameValues.Get(TokenResponse.ResponseParameters.AUTHCODE.ToString());

        var cscMatch = nameValues.Get(TokenResponse.ResponseParameters.CSCMATCH.ToString());

        // populating model with values
        var tokenResponse = new TokenResponse
        {
            Result = result,
            ResponseMessage = responseMessage,
            SecureToken = secureToken,
            TransactionIdentifierToken = requestId,
            TransactionType = transactionType,
            ReferenceCode = reference,
            AuthorizationCode = authCode,
            CSCMatch = cscMatch
        };

        return tokenResponse;
    }
Run Code Online (Sandbox Code Playgroud)

第4步和第5步:

回到客户端:

在这里,我使用从前面的步骤构建的URL,并使用jQuery添加最终所需的参数(安全信用卡信息),然后REDIRECT到Paypal.

 function SendCCDetailsToPaypal(secureParm) {

    //alert('in SendCCDetailsToPaypal:' + secureParm);

    var secureInfo = '&ACCT=' + $('#ccNumber').val() + '&EXPDATE=' + $("#expMonth").val() + $("#expYear").val() + "&CSC=" + $('#ccSecurityCode').val();
    secureInfo = secureParm + secureInfo;

    window.location.replace(secureInfo);               
}
Run Code Online (Sandbox Code Playgroud)

第6步:

Paypal将回发到以下方法之一:取消,错误或返回(在令牌请求中将方法命名为任何内容).解析响应并查看从Paypal返回的变量,特别是RESULT和RESPMSG.阅读文档了解具体细节,因为您可以合并地址验证和一系列其他功能.根据响应,显示适当的.

服务器端:

 public ActionResult Cancel()
    {
        var result = ParseRequest(HttpUtility.UrlDecode(Request.Params.ToString()));

        //return View("Return", result);
    }


    public ActionResult Error()
    {

        var result = ParseRequest(HttpUtility.UrlDecode(Request.Params.ToString()));

        return View("Return", result);
    }


    public ActionResult Return()
    {
        var result = ParseRequest(HttpUtility.UrlDecode(Request.Params.ToString()));

        return View("Return", result);
    }
Run Code Online (Sandbox Code Playgroud)

希望这有帮助,祝你好运!我会尽可能地回答澄清问题.感谢您查看此内容,并记得向前付款.


小智 5

我能够使用 RichieMN 的答案来实现透明重定向。但是,在SendCCDetailsToPaypal函数中使用window.location.replace进行重定向的问题是您在 GET 字符串上传递数据。

这适用于 PayFlow Gateway 端,但是当他们将客户的浏览器发送回您的 ResponseURL 时,您的 Apache 日志将显示整个payflowlink.paypal.com URL,包括 GET 字符串作为 Apache 访问日志中的引荐来源网址!该 GET 字符串包含信用卡号,现在您已经失去了 PCI 合规性!

为了缓解这个问题,您可以将 SecureToken 和 SecureTokenID 放入您的信用卡输入表单中,并将其直接 POST 到 payflowlink.paypal.com,或者您可以重写SendCCDetailsToPaypal构建表单并提交,如下所示:

function SendCCDetailsToPaypal() {
    var parameters = {
        "SECURETOKEN": secureToken,
        "SECURETOKENID": secureTokenID,
        "ACCT": $("#ccNumber").val(),
        "EXPDATE": $("#expMonth").val() + $("#expYear").val(),
        "CSC": $("#ccSecurityCode").val()
    };
    var form = $('<form></form>');
    form.attr("method", "post");
    form.attr("action", "https://pilot-payflowlink.paypal.com");
    $.each(parameters, function(key, value) {
        var field = $('<input></input>');
        field.attr("type", "hidden");
        field.attr("name", key);
        field.attr("value", value);
        form.append(field);
    });
    $(document.body).append(form);
    form.submit();
}
Run Code Online (Sandbox Code Playgroud)

由于该表单通过 POST 传输数据,因此当您的服务器收到 POST 结果时,引荐来源网址不包含任何敏感数据,并且您的 PCI 合规性得以维持。