近日网站需要对接国际化的支付接口PayPal,折腾了几天把遇到的坑记录下以备后用。
1.第一步先到官网申请商家账户https://www.paypal.com/这里的具体步骤我不太清楚,毕竟不是我来申请的
2.因为我们这里是网站用到的,使用了最便捷的网站付款标准版,其他的还可以用快速结账的API来实现。
首先我们把需要用到的参数信息配置到config中
<!--paypal支付--> <add key="payPalBusiness" value="你的账户"/> <add key="payPalReturnUrl" value="操作完成后的返回地址"/> <add key="payPalNotifyUrl" value="操作完成后的异步通知页面"/>
然后我们构建需要我们进行Post的页面代码,将必须填写的值存放到隐藏域<input type="hidden" />之中
string webUrl = CFun.GetAppStr("webUrl"); string business = CFun.GetAppStr("payPalBusiness"); string returnUrl = CFun.GetAppStr("payPalReturnUrl"); string notifyUrl = CFun.GetAppStr("payPalNotifyUrl"); Dictionary<string, string> sParaTemp = new Dictionary<string, string>(); sParaTemp.Add("cmd", "_xclick"); //按钮类型,包含:_xclick单个商品立即购买、_xclick_subscription订阅、_cart购物车、s_x-click加密 sParaTemp.Add("business", business); //商户名称 sParaTemp.Add("item_name", product_name); //商品名称 sParaTemp.Add("item_number", out_trade_no); //商品编号 sParaTemp.Add("currency_code", "USD"); //货币类型,默认美元 sParaTemp.Add("amount", payMoney.ToString()); //付款金额 sParaTemp.Add("notify_url", notifyUrl); //通知地址 sParaTemp.Add("cancel_return", webUrl); //取消返回地址 sParaTemp.Add("return", returnUrl); //返回地址 string submitHtml = PayPalSubmit.BuildRequest(sParaTemp, "post"); Response.Write(submitHtml);
/// <summary>
/// 建立请求,以表单HTML形式构造(默认)
/// </summary>
/// <param name="sParaTemp">请求参数数组</param>
/// <param name="strMethod">提交方式。两个值可选:post、get</param>
/// <param name="strButtonValue">确认按钮显示文字</param>
/// <returns>提交表单HTML文本</returns>
public static string BuildRequest(Dictionary<string, string> dicPara, string strMethod)
{
//正式的是https://www.paypal.com/cgi-bin/webscr,这里我们用测试的地址进行测试
string actionUrl = "https://www.sandbox.paypal.com/cgi-bin/webscr";
StringBuilder sbHtml = new StringBuilder();
sbHtml.Append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />");
sbHtml.Append("<form id='paypalsubmit' name='alipaysubmit' action='" + actionUrl + "' method='" + strMethod.ToLower().Trim() + "'>");
foreach (KeyValuePair<string, string> temp in dicPara)
{
sbHtml.Append("<input type='hidden' name='" + temp.Key + "' value='" + temp.Value + "'/>");
}
//submit按钮控件请不要含有name属性
sbHtml.Append("<input type='submit' style='display:none;'></form>");
sbHtml.Append("<script>document.forms['paypalsubmit'].submit();</script>");
return sbHtml.ToString();
}
这几个参数是比较重要的,更全面的参数介绍可以去官网查找相关文档
3.发起代码可以了,我们接下来写一下返回和通知页面的代码
在通知页面我们可以获取到下面几个关键参数
string item_number = CFun.RequestPamStr("item_number"); //商品编号
string pay_order = CFun.RequestPamStr("tx"); //paypal交易编号
string pay_status = CFun.RequestPamStr("st"); //交易状态
我们可以通过item_number参数跟数据库做数据对应,根据pay_order做验证防止重复使用,根据pay_status判断是否交易成功(pay_status="Completed")
这里有一点要注意,PayPal默认的是没有自动返回的,需要进行相关配置。操作步骤:用户信息-->销售通知-->网站付款习惯设定
将图中两处设置为开启即可。
4.返回页面一般只是用来显示充值结果,处理相关业务逻辑我们一般要放在通知页面进行操作。
string response=ValidateSource();
if (response == "VERIFIED") //信息验证成功
{
string sendData = response;
string item_number = CFun.RequestPamStr("item_number"); //商品编号
string pay_order = CFun.RequestPamStr("txn_id"); //paypal交易编号
string pay_status = CFun.RequestPamStr("payment_status"); //交易状态
ErrorLog.sendLog(new Exception(),"商品编号:"+item_number+";交易编号:"+pay_order+"交易状态:"+pay_status);
if (pay_status == "Completed") //付款成功
{
//业务处理
}
Response.Write(sendData);
}
/// <summary>
/// 验证返回地址是否是官方返回
/// </summary>
/// <returns></returns>
private string ValidateSource()
{
//Post back to either sandbox or live
string strSandbox = "https://www.sandbox.paypal.com/cgi-bin/webscr";
string strLive = "https://www.paypal.com/cgi-bin/webscr";
//System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; //Framework4.5支持
ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072; //4.0写法
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strSandbox);
//Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param = Request.BinaryRead(HttpContext.Current.Request.ContentLength);
string strRequest = Encoding.ASCII.GetString(param);
string ipnPost = strRequest;
strRequest += "&cmd=_notify-validate";
req.ContentLength = strRequest.Length;
//for proxy
//WebProxy proxy = new WebProxy(new Uri("http://url:port#"));
//req.Proxy = proxy;
//Send the request to PayPal and get the response
StreamWriter streamOut = new StreamWriter(req.GetRequestStream(),
System.Text.Encoding.ASCII);
streamOut.Write(strRequest);
streamOut.Close();
StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream());
string strResponse = streamIn.ReadToEnd();
streamIn.Close();
return strResponse;
}
ValidateSource方法主要是通过将获取到的参数+"&cmd=_notify-validate"后调用接口进行验证,查看参数传递过程中是否被篡改,如果返回VERIFIED证明参数信息一致。
注意上面标红的地方,这个一定不能少,我们访问的https接口,如果少了上面的代码会报异常:请求被中止: 未能创建 SSL/TLS 安全通道
好了,上面代码写完后我们就可以发布到服务器进行测试了,paypal的测试还是比较好的,给我们提供了专门的测试地址,可以任意添加商户和个人账户来进行测试。
首先我们用我们注册的账户来登录:https://developer.paypal.com/
登录后我们找到
这里会给我们默认两个账户,当然你也可以做任意的修改,然后我们就可以用我们设定的账户进行测试了
测试完成以后如果我们想登录我们的测试账户进行信息查看就需要用到这个网址:https://www.sandbox.paypal.com
我们这里用商户账号进行登录,在这里我们同样需要把步骤3上面的配置信息再次进行操作。
在这里我们还能查看我们的IPN信息