UnionPay,ChinaPay 最新 银联支付接口C#\Asp.net\MVC 版本
1.概念普及
一、理解什么是UnionPay、ChinaPay
这两个概念如果搞不清楚,绝对够你瞎折腾一段时间的。
UnionPay:中国银联,最大的机构;他本身也提供系统接口但都是B2B的,对于单个商户他们不提供客服,也不提供技术解决,更不会提供商户后台(可查消费记录等);但他的技术接口文档比较齐全,而且也可以使用,警惕不要使用这些接口。
ChinaPay:银联电子支付公司,第三方的支付公司,UnionPay的所有接口和服务都托管给类似的第三方公司,ChinaPay再向商户服务,ChinaPay有自己的接口标准,但是非常不完善,目前只有java版本的案例;并且从UnionPay官网开通商户后,默认根据地区会自动转到诸如“ChinaPay”这样的第三方支付公司,后面的事情全由ChinaPay代管。
2.ChinaPay商户后台
开通商户之后,一般销售会发送邮件给你,一般邮件内容包含开户名称、技术支持联系方式等,另附带一个压缩包,主要包括如下内容:
txt:登录账号信息
cp.cer:加密公钥
NetPayClient:.Net的组件和properity配置文件
doc:接口文档等
logo:按钮的标准图片
3.登录ChinaPay商户后台获取交易证书
http://merchant.chinapay.com ,建议用IE或者FireFox登录,需要先下载和安装证书,安装ActiveX控件,登录证书只提供两份,多余两份的请联系销售;
登录成功之后可以看到订单、退款单等账单;
要实现支付接口,必须先获取一个“交易证书”
点击交易申请证书,获取到证书之后,需要将证书的上传回商户后台,同时本地需要导出证书私钥(带密码),以备用。
4.RSA公钥私钥加密
本地商城等和ChinaPay接口通信的时候都需要对数据进行加密和验证有效性。这里就牵扯加密的知识了。
RSA公钥私钥知识只需要记住下面这三点即可
1.一个端有公钥和私钥两个文件(或者两个字符串),通信时候可以用公钥或者私钥加密
2.公钥加密数据发送,私钥解密:保证信息的完整性和保密性,保证加密后的信息第三方劫持后无法查看内容,例如邮件等。
3.私钥加密数据发送,公钥解密:保证数据来源可靠,对信息进行签名,chinapay即使利用此方式,常用于公告、群发等操作。
5.准备工作
将NetPayClient文件的dll引用到你的项目,在程序里创建一个chinapay的文件夹,放入ChinaPay的公钥(cp.cer)、私钥(交易证书导出的pfx文件,有密码),以及security.properties文件
security.properties,打开修改具体的cer、pfx文件的路径以及私钥密码
6.付款流程
跟其他产品一样,根据参数构造一个form,然后提交到目标url中,提交同时加入签名。
log.Debug("开始支付...");
string payUrl = "https://payment.chinapay.com/CTITS/service/rest/page/nref/000000000017/0/0/0/0/0";
Hashtable myMap = new Hashtable();
myMap.Add("MerId", ChinaPayHelper.merchantCode);
string billNO = "CO" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
myMap.Add("MerOrderNo", billNO);
myMap.Add("TranDate", DateTime.Now.ToString("yyyyMMdd"));//交易日期
myMap.Add("TranTime", DateTime.Now.ToString("HHmmss"));//交易时间
myMap.Add("OrderAmt", "20");
myMap.Add("TranType", "0001");
myMap.Add("BusiType", "0001");
myMap.Add("MerPageUrl", Request.Url.Scheme + "://" + Request.Url.Authority + "/Home/PayResult");
myMap.Add("MerBgUrl", Request.Url.Scheme + "://" + Request.Url.Authority + "/Home/PayBackRcv");
myMap.Add("CurryNo", "CNY"); myMap.Add("PayTimeOut", "145");
myMap.Add("Version", "20140728");
myMap.Add("CommodityMsg", "ChinaPay测试-商品信息");
myMap.Add("MerResv", "ChinaPay测试-商户保留域");
//myMap.Add("TranReserved", "{\"Referred\":\"www.chinapay.com\",\"BusiId\":\"0001\",\"TimeStamp\":\"1438915150976\",\"Remoteputr\":\"172.16.9.44\"}"); /***********************/
//坑1:这里注意,如果采用chinpay自带的签名,必须在服务器的浏览器上获取交易私钥,否则由开发环境转到生产环境会出错!!!此坑注意
//chinapaysecure.SecssUtil su = new chinapaysecure.SecssUtil();
//string path = Server.MapPath("/cer/security.properties");
//log.Debug(path);
//bool flag = su.init(path);
//su.sign(myMap);
//if ("00" != su.getErrCode())
//{
// log.Error(su.getErrCode() + "=" + su.getErrMsg());
// ViewBag.ChinaPay = "签名过程发生错误,错误信息为-->" + su.getErrMsg();
// return View();
//}
//myMap.Add("Signature", su.getSign());
/*******************************************/ //以下为采用自己扩展的签名方法,无需关心测试还是生产环境的问题!!!
string signValue = ChinaPayHelper.Sign(myMap);
myMap.Add("Signature", signValue);
string sHtmlText = ChinaPayHelper.BuildRequest(myMap, payUrl);
Response.Write(sHtmlText);
支付成功,前后台通知页面
//同步通知方法
public ActionResult PayResult()
{
NameValueCollection coll = Request.Form;
string[] requestItem = coll.AllKeys;
Hashtable myMap = new Hashtable();
for (int i = 0; i < requestItem.Length; i++)
{
myMap.Add(requestItem[i], Request.Form[requestItem[i]]);//前台方法接收参数无需UrlDecode,但异步方法必须转换
}
SecssUtil su = new SecssUtil();
su.init(Server.MapPath("/ChinaPay/CERS/security.properties"));
su.verify(myMap);
string billNo = myMap["MerOrderNo"].ToString();
string billId = myMap["MerResv"].ToString();
if ("00" != su.getErrCode())
{
log.Error("ChinaPayReturn银联支付返回数据验证失败:" + billNo + ";" + billId + ";" + su.getErrCode() + su.getErrMsg());
ViewBag.ChinaPayResult = "ChinaPayReturn银联支付返回数据验证失败";
return View();
}
StringBuilder sbHtml = new StringBuilder();
sbHtml.Append("<table>");
foreach (DictionaryEntry de in myMap)
{
sbHtml.Append("<tr><td>" + de.Key.ToString() + "</td><td>" + Server.UrlDecode(de.Value.ToString()) + "</td></tr>");
}
sbHtml.Append("</table>");
ViewBag.ChinaPayResult=sbHtml.ToString();
return View();
} //异步后台方法,用于接收支付成功和退款成功的通知
[HttpPost]
public void PayBackRcv()
{
NameValueCollection coll = Request.Form;
string[] requestItem = coll.AllKeys;
Hashtable myMap = new Hashtable();
for (int i = 0; i < requestItem.Length; i++)
{
log.Debug(requestItem[i] + "=" + HttpUtility.UrlDecode(Request.Form[requestItem[i]]));
myMap.Add(requestItem[i], HttpUtility.UrlDecode(Request.Form[requestItem[i]]));//需要UrlDecode,否则验签失败!!!
}
SecssUtil su = new SecssUtil();
su.init(Server.MapPath("/ChinaPay/CERS/security.properties"));
su.verify(myMap);
string billNo = myMap["MerOrderNo"].ToString();
string billId = myMap["MerResv"].ToString();
if ("00" != su.getErrCode())
{
log.Error("ChinaPayNotify银联支付返回数据验证失败:" + billNo + ";" + billId + ";" + su.getErrCode() + su.getErrMsg());
Response.Write("fail");
return;
}
//处理您的业务逻辑 //END
}
异步方法无需返回特定的code给chinapay,chinapay是根据httpstatus判断的,如果是200,都认为收到消息了。
坑2:如果出现签名失败,需要添加注册表权限,一般本机测试用管理员打开vs不存在这个问题,部署到服务器之后需要加权限
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Security
对这两条加入Network service的读写权限。
之后可以在Window日志中调试支付中碰到的问题了。
7. 退款
string cpRefundUrl = "https://payment.chinapay.com/CTITS/service/rest/forward/syn/000000000065/0/0/0/0/0";
Hashtable myMap = new Hashtable();
myMap.Add("MerId", ChinaPayHelper.merchantCode);
myMap.Add("MerOrderNo", "RO"+DateTime.Now.ToString("yyyyMMddHHmmssfff"));
myMap.Add("TranDate", DateTime.Now.ToString("yyyyMMdd"));//交易日期
myMap.Add("TranTime", DateTime.Now.ToString("HHmmss"));//交易时间 myMap.Add("OriOrderNo", "20151010152643514");//改成单号
myMap.Add("OriTranDate", "20151010");
myMap.Add("RefundAmt", "10");
//myMap.Add("OrderAmt", "10"); myMap.Add("TranType", "0401");
myMap.Add("BusiType", "0001");
myMap.Add("MerResv", Guid.NewGuid().ToString()); myMap.Add("MerBgUrl", Request.Url.Scheme + "://" + Request.Url.Authority + "/Home/PayBackRcv");
myMap.Add("CurryNo", "CNY");
myMap.Add("Version", "20140728"); string param = ChinaPayHelper.sort(myMap, null);
String chkValue = ChinaPayHelper.Sign(myMap);
myMap.Add("Signature", chkValue);
param += "&Signature=" + HttpUtility.UrlEncode(chkValue);
log.Debug("银联退款:" + param);
string rst = ChinaPayHelper.Post(cpRefundUrl + "?" + param, "");
log.Debug(rst);
Hashtable rstMap = ChinaPayHelper.Str2HashMap(rst);
if ("0000,1022,1003".Contains(rstMap["respCode"].ToString()))
{
return View(("[" + rstMap["respCode"].ToString() + "-" + rstMap["respMsg"].ToString() + "] 银联退款已提交,退款成功后将自动返回钱款到客户银联卡!"));
}
else
{
return View(("[" + rstMap["respCode"].ToString() + "-" + rstMap["respMsg"].ToString() + "] 银联退款失败!"));
}
注意修改原始单号和原始交易日期以及退款金额:
myMap.Add("OriOrderNo", "20151010152643514");//改成单号
myMap.Add("OriTranDate", "20151010");
myMap.Add("RefundAmt", "10"); 一般退款会在24小时内完成
8.退款没有异步通知???
真碰到过,联系客服之后,说是接口问题,他们修复好了之后便正常了,所以如果遇到什么问题,实在解决不了,赶紧电话客服,chinapay不是那么靠谱的。。。。!!!
9.自己封装的工具类,除了sign是override官方的,其他都是官方源文件里复制除了的方法,部分方法值得优化哦
using chinapaysecure;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web;
/******
jackchain
jackchain@chinacloudtech.com
2015-10-08
*****/
namespace ZPS.Tools
{
public class ChinaPayHelper
{
#region 基础配置
public static string merchantCode = ""; //商户号 private static string privateKeyPath = "/ChinaPay/CERS/donoratico.pfx"; //私钥文件地址
private static string privateKeyPwd = "byby1231818"; //私钥密码
#endregion /// <summary>
/// 签名,注意官方给出的签名方法,要求私钥必须是安装在服务器的,假若安装在本地测试机器上,等发布到生产环境中后会出现证书无法使用的错误
/// </summary>
/// <param name="param">需要加密的字符串</param>
/// <returns></returns>
public static string Sign(Hashtable myMap)
{
string param = sort(myMap, null);
X509Certificate2 certificate = new X509Certificate2(System.Web.HttpContext.Current.Server.MapPath(privateKeyPath), privateKeyPwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
RSAParameters key = ((RSACryptoServiceProvider)certificate.PrivateKey).ExportParameters(true);
return Convert.ToBase64String(HashAndSignBytes(Encoding.UTF8.GetBytes(param), key));
} /// <summary>
/// 构造提交表单
/// </summary>
/// <param name="myMap"></param>
/// <param name="actionUrl"></param>
/// <returns></returns>
public static string BuildRequest(Hashtable myMap, string actionUrl)
{
StringBuilder sbHtml = new StringBuilder();
sbHtml.Append("<form id='cpsubmit' name='cpsubmit' action='" + actionUrl + "' method='POST'>");
foreach (DictionaryEntry de in myMap)
{
sbHtml.Append("<input type='hidden' name='" + de.Key.ToString() + "' value='" + de.Value.ToString() + "'/>");
}
//submit按钮控件请不要含有name属性
sbHtml.Append("<input type='submit' value='CHINAPAY' style='display:none;'></form>");
sbHtml.Append("<script>document.forms['cpsubmit'].submit();</script>");
return sbHtml.ToString();
} /// <summary>
/// 调用远程Restful服务
/// </summary>
/// <param name="url">url地址</param>
/// <param name="param">参数</param>
/// <param name="time">超时时间</param>
/// <returns></returns>
public static string Post(string url, string param, int time = )
{
Uri address = new Uri(url);
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json;charset=utf-8"; //"application/x-www-form-urlencoded";
request.Timeout = time;
byte[] byteData = UTF8Encoding.UTF8.GetBytes(param == null ? "" : param);
request.ContentLength = byteData.Length; using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, , byteData.Length);
}
string result = "";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
result = reader.ReadToEnd();
}
return (result);
} /// <summary>
/// 银联应答参数转换为哈希表
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
public static Hashtable Str2HashMap(string param)
{
string[] kv = param.Split('&');
Hashtable map = new Hashtable();
foreach (string str in kv)
{
string[] temp = str.Split('=');
if (temp != null && temp.Length >= )
{
map.Add(temp[], temp[]);
}
}
return map;
} #region 以下方法ChinaPay
private static byte[] HashAndSignBytes(byte[] DataToSign, RSAParameters Key)
{
try
{
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.ImportParameters(Key);
return provider.SignData(DataToSign, new SHA512CryptoServiceProvider());
}
catch (CryptographicException exception)
{
Console.WriteLine(exception.Message);
return null;
}
} public static string sort(Hashtable paramHashTable, string[] invalidList)
{
IDictionaryEnumerator enumerator = paramHashTable.GetEnumerator();
ArrayList list = new ArrayList();
while (enumerator.MoveNext())
{
string str = enumerator.Key.ToString();
enumerator.Value.ToString();
if (((invalidList == null) || (invalidList.Length <= )) || !invalidList.Contains<string>(str))
{
list.Add(str);
}
}
IComparer comparer = new myReverserClass();
list.Sort(comparer);
string str2 = string.Empty;
for (int i = ; i < list.Count; i++)
{
string str3 = list[i].ToString();
string str4 = paramHashTable[str3].ToString();
if (i == (list.Count - ))
{
str2 = str2 + str3 + "=" + str4;
}
else
{
string str5 = str2;
str2 = str5 + str3 + "=" + str4 + "&";
}
}
StringBuilder builder = new StringBuilder(str2);
return builder.ToString();
}
#endregion
}
}
11.总结
1.理解unionpay、chinapay可以少走很多弯路
2.理解RSA方法、警惕交易证书申请和安装,否则很可能导致测试环境能用、正式环境无法sign
3.服务器注册表权限,EventLog需要添加IIS权限
4.多问客服
一个不小的支付公司,却没有全语言的demo,真是悲哀,只好贡献一个.Net的了(MVC)下载demo http://download.csdn.net/detail/ovenj/9186317
UnionPay,ChinaPay 最新 银联支付接口C#\Asp.net\MVC 版本的更多相关文章
- ASP.NET MVC 下使用支付宝支付接口 以及 ASP.NET Core 下相关改造支付
通过nuget首先引用AopSdk.dll 包 下面写的是 Asp.Net MVC 下相关的支付接口 APP支付 配置客户端相关的参数,配置成自己的代码就可以了 private string APPI ...
- Asp.net MVC 版本简史
http://www.dotnet-tricks.com/Tutorial/mvc/XWX7210713-A-brief-history-of-Asp.Net-MVC-framework.html A ...
- 微信个人支付接口---YunGouOS 1.1.3 版本发布,新增个人微信/支付宝收款接口
软件接口 https://www.oschina.net/news/113305/yungouos-1-1-3-released 文档说明 https://www.oschina.net/p/Yu ...
- Phonegap 之 iOS银联在线支付(js调用ios端银联支付控件)
Phonegap项目,做支付的时候,当把网站打包到ios或android端成app后,在app上通过wap调用银联在线存在一个问题: 就是当从银联支付成功后,再从服务器返回到app客户端就很难实现. ...
- ASP.NET MVC 5 02 - ASP.NET MVC 1-5 各版本特点
参考书籍:<ASP.NET MVC 4 高级编程>.<ASP.NET MVC 5 高级编程>.<C#高级编程(第8版)>.<使用ASP.NET MVC开发企业 ...
- ASP.NET MVC 5 01 - ASP.NET概述
本篇目录: ASP.NET 概述 .NET Framework 与 ASP.NET ASP.NET MVC简介 ASP.NET的特色和优势 典型案例 ▁▃▅ ASP.NET概述 ▅▃▁ 目前开发B/S ...
- ASP.NET MVC下的四种验证编程方式[续篇]
在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注Validation ...
- (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性
转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...
- 习题-任务2初始ASP.NET MVC项目开发
一.选择题 1.在ASP.NET MVC项目的RouteConfig.cs文件中,( )方法注册了默认的路由配置. A.RegisterMap B.RegisterRoutes C. ...
随机推荐
- [2011山东ACM省赛] Sequence (动态规划)
Sequence Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 Given an integer number sequence ...
- JBoss 系列四十八:JBoss 7/WildFly 使用TCP构建集群
我知道JBoss 集群Default 的设定就是UDP(JGroups),但在实际环境中的网络环境时常不允许UDP,在这种情况下,我们就需要使用TCP. JBoss 7/WildFly 中负责集群的主 ...
- C++中复制构造函数
复制构造函数 复制构造函数用于: 根据另一个同类型的对象显示或隐式初始化一个对象 复制一个对象,将它作为实参传给一个函数 从函数返回时复制一个对象 初始化顺序容器中的元素 根据元素初始化式列表初始化数 ...
- hdu 4651 - Partition(五边形数定理)
定理详见维基百科....http://zh.wikipedia.org/wiki/%E4%BA%94%E9%82%8A%E5%BD%A2%E6%95%B8%E5%AE%9A%E7%90%86 代码如下 ...
- ORACLE恢复神器之ODU/AUL/DUL
分享ORACLE数据库恢复神器之ODU.DUL和AUL工具. ODU:ORACLE DATABASE UNLOADER DUL:DATA UNLOADER AUL:也称MyDUL 关于三种工具说明: ...
- TypeScript的全部资料,以后都放这儿了
很早之前就听说TypeScript了(以下简称TS),但总是用难以抽出时间给自己找到这个冠冕堂皇的理由.最近又心血来潮,打算写TS的博客了,毕竟TS核心开发者也是C#之父,像我这么热爱C#的人,怎么可 ...
- kali linux 渗透测试视频教程 第五课 社会工程学工具集
第五课 社会工程学工具集 文/玄魂 教程地址:http://edu.51cto.com/course/course_id-1887.html 目录 第五课社会工程学工具集 SET SET的社会工程 ...
- 深入理解java虚拟机【垃圾回收算法】
Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...
- RabbitMQ(二) -- Work Queues
RabbitMQ(一) -- Work Queues RabbitMQ使用Work Queues的主要目的是为了避免资源使用密集的任务,它不同于定时任务处理的方式,而是把任务封装为消息添加到队列中.而 ...
- [WinAPI] 串口1-创建[包括: 打不开串口]
本来是用一个USB扩展把一个USB括成4个,然后把USB转串口连接上,虽然设备管理器可以找到用SSCOM也能找到,但是用API就是打不开,最后把USB转串插在电脑的一个USB上就可以啦! #inclu ...