我正在尝试使用Payflow Pro(https://pilot-payflowpro.paypal.com)尝试成为PCI兼容的C#VS2012 Framework 4.5 MVC应用程序。我们多年来一直在使用PayflowPro,这就是我必须使用的。从我的阅读中看来,我似乎应该使用透明重定向,所以我没有在我的网络服务器上发布任何私密内容,但我不知道我是否需要这些以及我希望如何处理这一点。我也有几个问题......
我认为这一切都有效: 我的理解是你需要一个securetoken(与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,然后是' Sale'旅行#2?
所以这里有一些非常粗糙的编码类型: 对于我的R& D测试,我构建自己的名称/值对参数字符串(见下文),并通过Webbox通过他们的沙箱/测试网址(pilot-payflowpro.paypal.com)与网关服务器进行通信。我得到了一个成功的回复和SECURETOKEN回来了。安全令牌的初始请求(如下所示)为TRXTYPE = A(授权),不发送卡信息。我想先授权吗?
以下是我的参数(也可能包含发货信息,但未在下面列出):
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
正如我所说,我得到了一个成功的回复并转到下一步发送安全数据(CC#,EXPDATE,安全码)。当我从params中删除我的UN / PW / VENDOR /合作伙伴信息时,由于无效的用户身份验证而出现错误。但是,看到我动态建立第二个电话,我无法在那里使用我的PayPal / pw。我错过了什么?任何人都可以提供上述此问题或其他问题的帮助吗?
如果我需要添加任何说明,请告诉我。提前感谢您的时间!
答案 0 :(得分:6)
在Paypal工程师花了很多时间之后,我成功找到了Paypal的Payflow透明重定向解决方案,没有托管页面(有自己的支付页面)。同样,按照工程师的说法,这里的文档非常令人困惑:Payflow API Documentation。此外,代码没有优化,因为它只是一个R& D应用程序,但作为一个整体,它对我有用。只是一个例子和解释,我相信有更好的方法来做个别的步骤。希望这会有所帮助,并允许您绕过一些减慢Paypal Payflow集成速度的障碍。
是的,它符合PCI标准,因为没有安全的客户数据会打到您自己的服务器上。请记住,PCI合规性非常复杂且涉及,但这是其中很重要的一部分。好的,所以我将解释我在MVC C#环境中做了哪些工作。我将解释这里的步骤,然后包括下面的代码。
简单,对吗?或许,但对我而言,第5步给我带来了很大的问题。我正在使用POST,但不明白为什么我一直在回复错误。这是一个关于无效商家或身份验证的HTML页面。请记住重定向,而不是发布第5步。
<强> CODE 强>:
第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
});}
服务器端:
我有单独的方法用于构建令牌请求所需的所有参数值。前三个构建:身份验证,事务详细信息,透明重定向。我在web.config文件中保留urls和payflow acct信息。最后一个方法ProcessTokenTransaction通过WebRequest完成与Paypal联系的所有繁重工作,然后将其解析为将发送回客户端的URL。这种方法应该重构为更清洁的交付,但我会留给你。 ParseResponse是一种填充我创建的简单模型的方法,并返回该模型。
令牌(沙盒)的网址: https://pilot-payflowpro.paypal.com
这与令牌网址不同!!用于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;
}
第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);
}
第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);
}
希望这有帮助,祝你好运!我会尽可能地回答澄清问题。感谢您查看此内容,并记得向前付款。
答案 1 :(得分:4)
我能够使用RichieMN的答案来实现透明重定向。但是,在 SendCCDetailsToPaypal 函数中使用 window.location.replace 进行重定向的问题是您在GET字符串上传递数据。
这适用于PayFlow网关端,但当他们将客户的浏览器发送回您的ResponseURL时,您的Apache日志将显示整个payflowlink.paypal.com网址,包括GET字符串作为Apache访问日志中的引荐来源!该GET字符串包含信用卡号,现在您刚刚失去了PCI合规性!
要解决此问题,您可以将SecureToken和SecureTokenID放入信用卡输入表单,然后将其直接发送到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();
}
由于该表单通过POST传输数据,当您的服务器返回结果POST时,引用者不包含任何敏感数据,并且您的PCI合规性得以维持。