支付宝 中文转义字符商品名称 需要转义吗

标签:至少1个,最多5个
近期公司的APP打算上线,需要集成支付的功能。由于采用的是Python进行开发,因此无法直接使用官方提供的SDK。虽然也有一些集成的第3方可以使用,比如、。
但是由于提供的时间比较充裕,于是就自己实现了1个。在这个过程中,难免遇到一些坑,而这些坑有时会困扰你很久。
最初,并没有打算写这么一篇文章,因为它的适用范围很窄。但是网上搜索到的关于APP支付方面的都是移动端iOS和Android的实现方式,对于服务端的实现寥寥无几。相比而言,python在当前毕竟是小众语言,而如果参考其他语言,比如php的实现,发现这个过程还是有不少地方是没有讲清楚的。
虽然对于很多开发者来说,支付这个功能涉及的知识点并不是很多,但是你会发现你却在这里耗费了很多的时间。有时1个签名的问题,就让你无法调用支付,比如支付宝的Alipay10问题,总是出现服务器繁忙的提示,其实就是你的签名出了问题。
在这里,由于涉及到公司的一些敏感信息的问题,因此下面代码中的签名用的都是测试数据,而签名是根据已经验证通过的函数调用计算出来的。当你发现自己签名不过时,可以直接复制这些字符串,然后比对下面计算出来的签名来查看你的签名函数及你的回调处理哪里出了问题。
首先为了避免耽误大家的时间,这里我们只实现了及支付宝的。对于微信公众支付及支付宝的其他支付场景是不适用的。
这里,限于篇幅,只对订单支付及异步回调的部分进行说明,因为如果把所有的接口都过一遍,太耗费时间,还不如直接在pypi上上传1个包,直接使用pip安装。
在这里,将用到的签名的方式单独提取出来进行讲解,对于相同产品其他的接口也是适用的,只是请求的参数有所变化而已。
个人建议及使用的库
在正式讲述APP支付之前,我有如下的建议:
Python版本&=2.7.9,由于Python版本2.7.9为1个bug修复版本,在这个版本中使用新的SSL模块,修复了之前HTTP客户端模块(比如urllib2,httplib)不对服务器证书进行校验的问题,详情请查看。
使用lxml,而不是标准库中的XML库,主要在于标准库中的XML模块无法检验恶意构造的数据,详情请查看。
使用pycrypto库用于支付宝RSA签名,版本&=2.61。这里使用的是pycrypto,是因为安装比较方便,另外因为版本2.61之前在某种情况下,使用fork会出现随机数不安全的问题,详情请查看。
下面我们需要理清我们要做的事情,避免不必要的工作。主要是如下2个方面:
服务端负责生成订单及签名,及接受支付异步通知
客户端负责使用服务端传来的订单信息调用支付接口,及根据SDK同步返回的支付结果展示结果页。
另外,私钥必须放在服务端,签名过程也必须放在服务端。
支付方式比较
在这2种支付方式中,我们需要对签名的信息(URL键值对,例如key1=value1&key2=valu2...)按照ASCII编码顺序进行排序后再进行签名,并且采用POST方式进行提交。
在微信中,签名的方式采用的是md5,而支付宝采用的RSA。
在微信支付中,提交和返回数据都为XML格式,其根节点为xml。而在支付宝中,采用的是使用表单提交的方式来进行。
由于微信支付采用的是XML格式,因此字符编码采用的是UTF-8,而支付宝需要指定参数_input_charset来指定编码,官方建议我们采用UTF-8。
下面我们正式进行APP支付流程的说明,在这个过程中,我们需要阅读官方提供的文档。这里我们从微信开始,因为相比支付宝,微信的支付调用更为简单些。
在进行模块代码编写之前,我们来看看官方提供的。换句话说,在我们调用接口后,我们需要给APP客户端返回prepayid及生成的签名,另外还有APP端接口中的其他字段。
这里,假设我们统一下单时请求参数如下:
appid=wx0ec43b&attach=支付测试&body=APP支付测试&mch_id=&nonce_str=1add1a30ac87aa2db72f57a2375d8fec&notify_url=http://wxpay./pub_v2/pay/notify.v2.php&out_trade_no=&spbill_create_ip=14.23.150.211&total_fee=1&trade_type=APP
而我们的商户号假设为,那么我们需要将商户号与之前的请求参数拼接在一起:
data = 'appid=wx0ec43b&attach=支付测试&body=APP支付测试&mch_id=&nonce_str=1add1a30ac87aa2db72f57a2375d8fec&notify_url=http://wxpay./pub_v2/pay/notify.v2.php&out_trade_no=&spbill_create_ip=14.23.150.211&total_fee=1&trade_type=APP&key='
&&& from hashlib import md5
&&& md5(data).hexdigest().upper()
'F3D12DF0DA652E97A766FA'
这里我们拼接后的参数进行MD5加密后将其转换为大写字母,这样就得到我们需要的签名了。因此,在请求统一下单时,我们需要传递如下的字符串:
&appid&wx0ec43b&/appid&
&attach&支付测试&/attach&
&body&APP支付测试&/body&
&mch_id&&/mch_id&
&nonce_str&1add1a30ac87aa2db72f57a2375d8fec&/nonce_str&
&notify_url&http://wxpay./pub_v2/pay/notify.v2.php&/notify_url&
&out_trade_no&&/out_trade_no&
&spbill_create_ip&14.23.150.211&/spbill_create_ip&
&total_fee&1&/total_fee&
&trade_type&APP&/trade_type&
&sign&F3D12DF0DA652E97A766FA&/sign&
关于签名校验,微信官方提供了1个,当在请求返回的err_code出现SIGNERROR时可以使用这个工具来辅助我们进行校验。
返回给客户端APP
当我们成功请求统一下单接口后,返回的结果可能如下所示:
&return_code&&![CDATA[SUCCESS]]&&/return_code&
&return_msg&&![CDATA[OK]]&&/return_msg&
&appid&&![CDATA[wx0ec43b]]&&/appid&
&mch_id&&![CDATA[]]&&/mch_id&
&nonce_str&&![CDATA[IITRi8Iabbblz1Jc]]&&/nonce_str&
&sign&&![CDATA[EB8ED0CED72F]]&&/sign&
&result_code&&![CDATA[SUCCESS]]&&/result_code&
&prepay_id&&![CDATA[wx507cbf6ffd8b]]&&/prepay_id&
&trade_type&&![CDATA[APP]]&&/trade_type&
接下来,我们需要取出返回结果中的prepay_id参数,然后按照接口中组装请求参数,假设我们得到如下的请求参数:
appid=wx0ec43b&noncestr=5K8264ILTKCH16CQ2&package=Sign=WXPay&partnerid=&prepayid=wx507cbf6ffd8b&timestamp=
那么进行签名后将得到字符串A2AA6D297FBAC。那么我们返回给客户端APP的字段主要有prepayid、noncestr、timestamp、sign。
当用户成功完成支付后,微信会将相关支付信息推送到在统一下单时提交的notify_url指定的url地址中。在这一步,我们主要要做的是检验信息,比如签名是否正确、支付金额是否相同,可以在这个过程中修改订单的支付状态。
如果检验通过后,我们需要给微信返回类似如下的参数:
&return_code&&![CDATA[SUCCESS]]&&/return_code&
&return_msg&&![CDATA[OK]]&&/return_msg&
在这一步可能遇到的问题是无法接收到微信推送过来的参数,由于这里公司采用的是Flask,因此采用如下的方式来进行接收:
from flask import request
@app.route('/notify')
def notify():
req = request.stream.read()
在这里,我采用的是从原始流中进行直接读取操作。
说完了微信,我们来看下支付宝的情况。
这里我采用UTF-8编码进行处理,并查看如下的,让我们对支付流程有1个了解。
在正式开始支付宝的支付之前,我们先来说下基础的一些内容,首先是要使用的私钥要是PKCS8格式的。然后是需要传递给支付宝的参数,其中基本参数partner、_input_charset、sign、sign_type、service这些属于基本参数,是必须要传递的参数。关于需要传递参数的内容请查阅。
支付待签名字符串生成
关于支付请求参数,我们可以查看下面的链接。
在支付宝APP支付中,我们需要请求参数中需要剔除sign_type、sign这2个参数,并在签名之前需要对字符串进行UTF-8的urlencode,即待签名字符串如果有中文则必须未转义形式显示,例如:
_input_charset="utf-8"&notify_url="http://notify.msp.hk/notify.htm"&out_trade_no="-6177"&partner="8364"&seller_id=""&service="mobile.securitypay.pay"&subject="测试"&payment_type="1"&total_fee="0.01"
在这里,我们对请求的参数进行了排序,然后请求的参数的数值需要添加双引号。之后,我们需要对上面的字符串进行签名处理,这里我们假设我们的私钥如下:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDQ3/XlPY/IFw8FISXKHVRLICPSEPmWCauMtKPoAc9M6szlCjG+
YqtxaigPwVdRqoG3m24uMgz36qXyANvXMB3X7e6t6g1DoI3wxy5aNNlE0Dlu0BIH
rcLUFsSZgCTuAvOori2oGVp6StXz0Wg5kacICnf6GNHCM1B2IgshEQte2wIDAQAB
AoGAMkbmanKiDFi4jdSHwxnCM38eAC+D1ECpoWnN1kexPWN7RFpq1NftSpRx5jD0
srynEqoAIHB9vKMnpJPeVvLHC8ZvtZyehQPTvdaqdeORcZUhaYHYBWgiCCr/6fgW
00yxR+UrYZFY6DEHbHkXgXqtEFzoVYIVwI6a90F/xFQ8hpECQQDoypOny/zUvocc
hTQ/JuqsmZXKNZgU+1c/3Kflz7RDpi9e94yR9eaBSLBTDEkngJkJD5/riTzC0O4A
Hb/2+5vzAkEA5bL5lgoCWyyVlvy/PBbZ2Ilcf+vMyvtyDBWklW9xrXEy53W+G4Qq
NSatTzNHN2VNEqFz2/3xNIbFlMpHzU3zeQJBAJS3thTgkKko/xANWQ9vQUT66WLB
UmM1HsxBn1GFm9gL9v9ojnlA6v10/pBPrPx7f0j2nmfOyO58o0+XseeLXlkCQB55
k2GTrGJaVPJ2UAzx3y86cjpKl54qpCP0TyTAZ22igiVxWqqd61en7QCABifUWdhp
8UwzsefNJbOq7sHPYMkCQACbuh1TKx9AlZz1kPoAagBsZofx4cb5QnHpmIzREbRd
aydfoaqR5BKpjJXky4tyBDeyp50s96UUd/eEYDC8RV4=
-----END RSA PRIVATE KEY-----
在RSA签名就验证签名中,我们需要确保公钥和私钥都包含在BEGIN和END之间,且不需要进行将其放在1行中。
然后我们使用如下的方式进行签名操作:
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from base64 import b64encode
message = '_input_charset="utf-8"&notify_url="http://notify.msp.hk/notify.htm"&out_trade_no="-6177"&partner="8364"&seller_id=""&service="mobile.securitypay.pay"&subject="测试"&payment_type="1"&total_fee="0.01"'
key = RSA.importKey(open('rsa_private_key.pem').read())
h = SHA.new()
h.update(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
print b64encode(signature)
这样我们将得到签名:
FDW1YrI/FeX841orIDZ+rYyacSyDtWs4d+GPNpEMbWd38TpmePLagEIzAd8DDB3TlLxwyiA/IgGYIiLPQOk8qdIdp3AkjWHEMPmRbULZx2bMVNJlJy/yunOAbJRIJhP3I1Ip/nCFRVvBmBE3I8Mt95UQtYhtLkx+fZbuXmpCckQ=
在这里,官方所说的是SHAWithRSA函数对应于PKCS1_V1_5标准外加SHA1加密方式,需要主要的是这里生成的私钥的长度是1024位。
然后我们对参数字符串进行拼接将得到:
_input_charset="utf-8"&notify_url="http://notify.msp.hk/notify.htm"&out_trade_no="-6177"&partner="8364"&seller_id=""&service="mobile.securitypay.pay"&subject="测试"&payment_type="1"&total_fee="0.01"&sign="FDW1YrI/FeX841orIDZ+rYyacSyDtWs4d+GPNpEMbWd38TpmePLagEIzAd8DDB3TlLxwyiA/IgGYIiLPQOk8qdIdp3AkjWHEMPmRbULZx2bMVNJlJy/yunOAbJRIJhP3I1Ip/nCFRVvBmBE3I8Mt95UQtYhtLkx+fZbuXmpCckQ="&sign_type="RSA"
我们将生成的这串字符串返回给客户端APP调用即可。
与微信一样,当用户成功支付后,支付宝会主动以POST方式将数据推送给你提交的notify_url中的URL。在这里,我们需要以表单的形式来接收传递过来的参数。
在此之前,我们说下一些关于通知的内容:
通知触发条件:支付宝只有在交易成功、支付成功以及交易创建是会触发通知,对于交易关闭时不触发通知的,换句话说在这些情况下会主动推送消息给你。
通知交易状态:主要有4种状态,TRADE_SUCCESS,TRADE_FINISHED、TRADE_CLOSED,WAIT_BUYER_PAY,分别对应交易成功、交易完成、交易关闭和等待买家付款。
而支付宝会传递过来的参数,我们可以查看。
在异步回调中,我们需要完成如下2个验证的工作:
验证是否是支付宝发来的通知
对于第2个验证,我们需要拼装成如下的URL:
/gateway.do?service=notify_verify&partner=2354&notify_id=RqPnCoPT3K9%252Fvwbh3I%252BFioE227%252BPfNMl8jwyZqMIiXQWxhOCmQ5MQO%252FWd93rvCB%252BaiGg
然后我们进行GET请求,而结果会返回1个true或false的字符串。
对于第1种验证,假设我们有如下的字符串:
discount=0.00&payment_type=8&subject=测试&trade_no=4842&buyer_email=&gmt_create= 14:45:23&notify_type=trade_status_sync&quantity=1&out_trade_no=710&seller_id=6263&notify_time= 14:45:24&body=测试测试&trade_status=TRADE_SUCCESS&is_total_fee_adjust=N&total_fee=1.00&gmt_payment= 14:45:24&seller_email=&price=1.00&buyer_id=5429&notify_id=64ce1b6ab92d00ede0ee56ade98fdf2f4c&use_coupon=N&sign_type=RSA&sign=1glihU9DPWee+UJ82u3+mw3Bdnr9u01at0M/xJnPsGuHh+JA5bk3zbWaoWhU6GmLab3dIM4JNdktTcEUI9/FBGhgfLO39BKX/eBCFQ3bXAmIZn4l26fiwoO613BptT44GTEtnPiQ6+tnLsGlVSrFZaLB9FVhrGfipH2SWJcnwYs=
我们剔除了sign和sign_type参数后,按照ASCII顺序进行排序,我们将得到如下的字符串:
body=测试测试&buyer_email=&buyer_id=5429&discount=0.00&gmt_create= 14:45:23&gmt_payment= 14:45:24&is_total_fee_adjust=N&notify_time= 14:45:24&notify_type=trade_status_sync&out_trade_no=710&payment_type=8&price=1.00&quantity=1&seller_email=&seller_id=6263&subject=测试&total_fee=1.00&trade_no=4842&trade_status=TRADE_SUCCESS&use_coupon=N
然后我们进行如下的验证签名:
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from base64 import b64decode
sign = '1glihU9DPWee+UJ82u3+mw3Bdnr9u01at0M/xJnPsGuHh+JA5bk3zbWaoWhU6GmLab3dIM4JNdktTcEUI9/FBGhgfLO39BKX/eBCFQ3bXAmIZn4l26fiwoO613BptT44GTEtnPiQ6+tnLsGlVSrFZaLB9FVhrGfipH2SWJcnwYs='
msg = 'body=测试测试&buyer_email=&buyer_id=5429&discount=0.00&gmt_create= 14:45:23&gmt_payment= 14:45:24&is_total_fee_adjust=N&notify_time= 14:45:24&notify_type=trade_status_sync&out_trade_no=710&payment_type=8&price=1.00&quantity=1&seller_email=&seller_id=6263&subject测试&total_fee=1.00&trade_no=4842&trade_status=TRADE_SUCCESS&use_coupon=N'
key = RSA.importKey(open('alipay_public_key.pem').read())
sign = b64decode(sign)
h = SHA.new(msg)
verifier = PKCS1_v1_5.new(key)
print verifier.verify(h,sign)
在这里,我们读取支付宝的公钥,然后对签名进行base64编码解密,然后进行比对操作,其结果为1个布尔值。
最后,如果2个检验都通过,我们需要返回给支付宝1个字符串success即可。
2 收藏&&|&&12
你可能感兴趣的文章
13 收藏,2.1k
70 收藏,11.2k
本作品 保留所有权利 。未获得许可人许可前,不允许他人复制、发行、展览和表演作品。不允许他人基于该作品创作演绎作品
签名之前需要对字符串进行UTF-8的urlencode,这段怎么写?
分享到微博?
技术专栏,帮你记录编程中的点滴,提升你对技术的理解收藏感兴趣的文章,丰富自己的知识库
明天提醒我
我要该,理由是:
扫扫下载 App支付宝 APP 支付集成文档 - 推酷
支付宝 APP 支付集成文档
适用于商家在App应用中集成支付宝支付功能。商家APP调用支付宝提供的SDK调用支付宝客户端内的支付模块,商家APP会跳转到支付宝中完成支付,支付完后跳回到商家APP内,最后展示支付结果。
本文档旨在指导开发者快速集成支付宝APP支付,包括开签约APP支付、服务器配置以及安卓端配置。
1、签约APP支付
APP要想接入支付宝支付功能需要签约支付宝的 APP支付 ,签约要符合申请的条件,根据操作提示完成申请,等待审核通过即可
签约链接:
2、创建应用并获取APPID
然后创建应用,创建成功后即可获取到APPID
创建应用地址:
3、添加功能
创建应用之后,还有给应用添加 APP支付功能
勾选APP支付,确定即可
4、上线应用
根据提示提交审核,等待审核通过即可,注意:
测试APP支付必须上线应用,因为沙箱测试不能测试APP支付,可能出现 ALI40247错误
,见链接:
点击上线,按照提示完成操作即可,如图:
5、配置密钥
参考链接:
生成之后,Java开发找到 pkcs8 格式的私钥签名, 去除头尾、换行和空格 ,作为开发者私钥
将公钥 去除头尾、换行和空格 ,填入如图所示的地方
填写之后,可以验证公钥的正确性, 根据图示操作即可:
6、了解业务流程
二、服务器配置
参考文档:
1、下载SDK
服务器SDK有不同的开发语言版本,下载相应的版本,如图:
2、引入jar包
下面以java为例
集成支付宝接口需要引入的文件是:
alipay-sdk-java*.jar
commons-logging-1.1.1.jar
若进一步了解代码实现请引入文件:
alipay-sdk-java*-source.jar
commons-logging-1.1.1-sources.jar
3、调用示例
传入服务器创建订单所需参数参考文档:
参考事例代码:
//实例化客户端
AlipayClient client = new DefaultAlipayClient(&/gateway.do&,APP_ID,APP_PRIVATE_KEY,&json&,&GBK&,ALIPAY_PUBLIC_KEY);
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
//此次只是参数展示,未进行字符串转义,实际情况下请转义
request.setBizContent(&
&primary_industry_name&:&IT科技/IT软件与服务&,
&primary_industry_code&:&&,
&secondary_industry_code&:&&,
&secondary_industry_name&:&IT科技/IT软件与服务&
AlipayTradeAppPayResponse response = client.execute(request);
//调用成功,则处理业务逻辑
if(response.isSuccess()){
三、Android端配置
参考文档:
1、下载SDK并导入
下载地址:
下载后导入工程中
2、注册Activity并添加权限
android:name=&com.alipay.sdk.app.H5PayActivity&
android:configChanges=&orientation|keyboardHidden|navigation&
android:exported=&false&
android:screenOrientation=&behind&&
&/activity&
android:name=&com.alipay.sdk.auth.AuthActivity&
android:configChanges=&orientation|keyboardHidden|navigation&
android:exported=&false&
android:screenOrientation=&behind&&
&/activity&
&uses-permissionandroid:name=&android.permission.INTERNET&/&
&uses-permissionandroid:name=&android.permission.ACCESS_NETWORK_STATE&/&
&uses-permissionandroid:name=&android.permission.ACCESS_WIFI_STATE&/&
&uses-permissionandroid:name=&android.permission.READ_PHONE_STATE&/&
&uses-permissionandroid:name=&android.permission.WRITE_EXTERNAL_STORAGE&/&
3、添加混淆规则
-libraryjars libs/alipaySDK-.jar
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
4、调用支付接口
需要在新线程中调用支付接口(可参考
获取 PayTask 支付对象调用支付( 支付行为需要在独立的非ui线程中执行 ),代码示例:
final String orderInfo =
// 订单信息
Runnable payRunnable = new Runnable() {
publicvoidrun(){
PayTask alipay = new PayTask(DemoActivity.this);
String result = alipay.payV2(orderInfo,true);
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
mHandler.sendMessage(msg);
// 必须异步调用
Thread payThread = new Thread(payRunnable);
payThread.start();
5、支付结果获取和处理
(1)、同步返回
商户应用客户端通过当前调用支付的Activity的Handler对象,通过它的回调函数获取支付结果。(可参考alipay_demo实现)
private Handler mHandler = new Handler() {
publicvoidhandleMessage(Message msg){
Result result = new Result((String) msg.obj);
Toast.makeText(DemoActivity.this, result.getResult(), Toast.LENGTH_LONG).show();
(2)、异步通知
商户需要提供一个http协议的接口,包含在请求支付的入参中,其key对应notify_url。
支付宝服务器在支付完成后,会以POST方式调用notify_url传输数据。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致支付宝开放平台C++方式接入
我的图书馆
支付宝开放平台C++方式接入
8690人阅读
版权声明:本文为博主原创文章,未经博主允许不得转载。
如果你想以C\C++方式接入支付宝开放平台调用支付宝的各种开放接口(比如收款等),那么你可以参考这个项目;
这个项目里帮你解决了参数组装、排序、OpenSSL加签验签、网络请求等问题,能帮助你轻松接入支付宝;
并且该项目依赖C\C++标准库STL以及几个开源的第三方库,代码精简(只有几个class),非常易于集成;
使用起来很简便:
#include&&iostream&&&#include&"stdlib.h"&&&&#define&_DEBUG&&&&#include&"openapi/openapi_client.h"&&&&using&namespace&&&&&/**&++++++++++++++++++++++++++++++++++++++++++++++++&**/&&/**&此处替换为开发者在支付宝开放平台申请的应用ID&**/&&string&appId&=&"8888";&&&&/**&此处替换为开发者使用openssl生成的rsa私钥&**/&&string&pKey&=&"-----BEGIN&RSA&PRIVATE&KEY-----\n"&&&&&&&&&&"MIICXQIBAAKBgQDTI0/RaV/YKWDGbKkQGYpD9I/UljBCCf3rWm09sXiif8MN5rLA\n"&&&&&&&&&&"3TjC4gZ478n6Dys5yO23h1HGVTWu+mQ8071+pwLHGQ+dyDNrGWR89VLb9yanOeRf\n"&&&&&&&&&&"efOcN19ATZgGAzheM28E/iqaYkh8F2NlCjOiZAsBG6eVvxachwVAQUIWwwIDAQAB\n"&&&&&&&&&&"AoGAdVr8Q46JenHNW50L/2niw1DNHUF5g0tgeo+hhpf9UH0pIrHnC3Iq2Y+eP1ww\n"&&&&&&&&&&"7K+/u/elwcwSNOYp159PVcvvV9LwPwH29DdH6KEWIDiyFpjbXPcMMFwgakyLnFTL\n"&&&&&&&&&&"sxxa6DYznFokT+IPkF6esoypa7VQFU1RIal5Sgphq7CGCDECQQDqyL3QjYT6ffLd\n"&&&&&&&&&&"NRiMBB13+eIxvXGy5AEQcH4pNt6kYHWONCWeZ34miNp2UliIBvBHZ1uuGoO4F/Jx\n"&&&&&&&&&&"2sWwWlSpAkEA5jeQGFx/RDzzi0qPMpSOR50d2IC4NbbresY+hgJEBbI6n5hPR1ts\n"&&&&&&&&&&"MUuO1e3L5I5rzRKNzD1um1DdSgmqaqmHiwJBANLnRpNsPRMjRqHtS0Kjg7E9mDIk\n"&&&&&&&&&&"Qll3NXmGA96T+oXgXFlEgLJ9tzV4Y/471GlFClyp/RI1oTMi19fstP7I9hkCQBjr\n"&&&&&&&&&&"bseUS5phVqN/QJzjA7uwwChNVqNJ15eEmgP7fs13C213GS3KMZ3sZdu2T9m/qN+b\n"&&&&&&&&&&"4Il5JN3fFPUMssu06h0CQQCGBdmtRLi+9ws57qTPHR/BdHGUxdBRWllc9sGVVaRw\n"&&&&&&&&&&"+EOMGXus6/BssTRjwplx7w8uUR0U3s1KYDJMHMCjW25x\n"&&&&&&&&&&"-----END&RSA&PRIVATE&KEY-----";&&&&/**&支付宝公钥,用来验证支付宝返回请求的合法性&**/&&string&aliPubKey&=&"-----BEGIN&PUBLIC&KEY-----\n"&&&&&&&&&&"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkr\n"&&&&&&&&&&"IvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsra\n"&&&&&&&&&&"prwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUr\n"&&&&&&&&&&"CmZYI/FCEa3/cNMW0QIDAQAB\n"&&&&&&&&&&"-----END&PUBLIC&KEY-----";&&&&/**&注:appid,私钥,支付宝公钥等信息建议不要写死在代码中&**/&&/**&这些信息应以配置等方式保存,此处写在代码中只是为了示例的简便&**/&&/**&++++++++++++++++++++++++++++++++++++++++++++++++&**/&&&&&&/**&some&examples&**/&&JsonMap&getPrecreateContent();&&&&int&main(int&argc,&char&*argv[])&&{&&&&&&&&/**&实例化OpenapiClient工具类&**/&&&&&&OpenapiClient&openapiClient(appId,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&pKey,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&OpenapiClient::default_url,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&OpenapiClient::default_charset,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&aliPubKey);&&&&&&&&/**&++++++++++++++++++++++++++++++++++++++++++++++++&**/&&&&&&/**&各个具体业务接口参数组装模式具体参看Openapi官方文档&**/&&&&&&/**&https://doc./&**/&&&&&&//&demo1:当面付预下单示例&&&&&&string&method&=&"alipay.trade.precreate";&&&&&&JsonMap&contentMap&=&getPrecreateContent();&&&&&&&&/**&++++++++++++++++++++++++++++++++++++++++++++++++&**/&&&&&&&&/**&++++++++++++++++++++++++++++++++++++++++++++++++&**/&&&&&&/**&网关扩展参数,例如商户需要回传notify_url等,可以在extendParamMap中传入&**/&&&&&&/**&这是一个可选项,如不需要,可不传&**/&&&&&&/*&StringMap&extendParamM&&&&&extendParamMap.insert(StringMap::value_type("notify_url",&"http://api.test.alipay.net/atinterface/receive_notify.htm"));&&&&&*/&&&&&&&&/**&++++++++++++++++++++++++++++++++++++++++++++++++&**/&&&&&&&&/**&调用Openapi网关&**/&&&&&&JsonMap&respM&&&&&&respMap&=&openapiClient.invoke(method,&contentMap);&&&&&&/*&如果有扩展参数,则按如下方式传入&&&&&respMap&=&openapiClient.invoke(method,&contentMap,&extendParamMap);&&&&&*/&&&&&&&&/**&解析支付宝返回报文&**/&&&&&&JsonMap::const_iterator&iter&=&respMap.find("code");&&&&&&if&(iter&!=&respMap.end())&{&&&&&&&&&&string&respCode&=&iter-&second.toString();&&&&&&&&&&DebugLog("code:%s",&respCode.c_str());&&&&&&}&else&{&&&&&&&&&&DebugLog("cannot&get&code&from&response");&&&&&&}&&&&&&&&iter&=&respMap.find("msg");&&&&&&if&(iter&!=&respMap.end())&{&&&&&&&&&&string&respMsg&=&iter-&second.toString();&&&&&&&&&&DebugLog("msg:%s",&respMsg.c_str());&&&&&&}&else&{&&&&&&&&&&DebugLog("cannot&get&msg&from&response");&&&&&&}&&&&&&&&system("pause");&&&&&&return&0;&&}&&&&/**&&*&组装支付宝预下单业务请求&&*/&&JsonMap&getPrecreateContent()&{&&&&&&&&JsonMap&contentM&&&&&&contentMap.insert(JsonMap::value_type(JsonType("out_trade_no"),&JsonType("12")));&&&&&&contentMap.insert(JsonMap::value_type(JsonType("total_amount"),&JsonType(0.01)));&&&&&&contentMap.insert(JsonMap::value_type(JsonType("subject"),&JsonType("好东西")));&&&&&&&&return&contentM&&}&&
Readme.txt:
该项目为C++项目,包含访问支付宝开放平台(Openapi)网关的源码;
/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ **/
工程中有两个项目文件:
Openapi.pro为QtCreater项目文件,可用QtCreater打开;
Openapi.sln为VS(2008版)项目文件,可用VS打开;
注:该项目源码依赖C\C++标准库(STL)以及几个开源的第三方库(cJSON,libcurl,openssl);
开发者可以在其它支持C\C++的平台(linux等)编译适配;
/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ **/
项目主要源码包含在目录“/openapi”中;
1)主工具类 -- ./openapi/openapi_client
---- 访问支付宝开放平台(openapi)网关的工具类;
---- 外部主要依赖该类访问支付宝网关;
---- 具体使用示例可参见main.cpp中的源码及注释;
2)其它依赖的工具类:
HttpClient -- ./openapi/http/http_client
---- 该工具类提供Http(Https)网络通信的功能;
---- 该工具类依赖第三方库libcurl
JsonUtil -- ./openapi/json/json_util
---- 该工具类提供Json串与C++对象之间的转换功能;
---- 该工具类依赖第三方库cJSON;
openssl & libcurl -- ./libs/
---- 这两个第三方库已经编译成静态库(windows平台);
---- 其它平台的静态库,开发者可自行下载源码进行编译;
/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ **/
集成/调试之前的准备工作:
1)入住支付宝开放平台,申请应用并获得应用ID(appId);
2)本地生成rsa密钥对,并上传公钥到支付宝开放平台相应的应用下,
并保存好自己的私钥(privateKey -- 严格保密);
rsa密钥生成方式参看支付宝开放平台官方文档;
注:C++使用的是标准格式的rsa私钥,不是pkcs8格式的;
3)各个具体业务接口参数组装模式具体参看Openapi官方文档;
4)支付宝开放平台文档中心:
其中关键工具类的构建如下:
#ifndef&OPENAPICLIENT_H&&#define&OPENAPICLIENT_H&&&&#include&"http/http_client.h"&&#include&"json/json_util.h"&&#include&&openssl/rsa.h&&&#include&&openssl/pem.h&&&#include&&openssl/err.h&&&#include&&time.h&&&&&#ifndef&XRSA_KEY_BITS&&#define&XRSA_KEY_BITS&(1024)&&#endif&&&&/**&STL&map&default&sort&order&by&key&**/&&typedef&map&string,&string&&StringM&&&&/**&&*&@brief&The&OpenapiClient&class&&*/&&class&OpenapiClient&{&&public:&&&&&&OpenapiClient(const&string&&appId,&&&&&&&&&&&&&&&&&&&&const&string&&privateKey,&&&&&&&&&&&&&&&&&&&&const&string&&url&=&default_url,&&&&&&&&&&&&&&&&&&&&const&string&&charset&=&default_charset,&&&&&&&&&&&&&&&&&&&&const&string&&alipayPublicKey&=&string());&&&&public:&&&&&&static&const&string&default_&&&&&&static&const&string&default_&&&&&&static&const&string&default_sign_&&&&&&static&const&string&default_&&&&&&&&static&const&string&KEY_APP_ID;&&&&&&static&const&string&KEY_METHOD;&&&&&&static&const&string&KEY_CHARSET;&&&&&&static&const&string&KEY_SIGN_TYPE;&&&&&&static&const&string&KEY_SIGN;&&&&&&static&const&string&KEY_TIMESTAMP;&&&&&&static&const&string&KEY_VERSION;&&&&&&static&const&string&KEY_BIZ_CONTENT;&&&&private:&&&&&&string&appId;&&&&&&string&privateK&&&&&&string&signT&&&&&&string&&&&&&&string&&&&&&&string&&&&&&&&&string&alipayPublicK&&&&public:&&&&&&string&invoke(const&string&&method,&const&string&&content,&const&StringMap&&extendParamMap&=&StringMap());&&&&&&JsonMap&invoke(const&string&&method,&const&JsonMap&&contentMap,&const&StringMap&&extendParamMap&=&StringMap());&&&&&&private:&&&&&&/**&&&&&&*&&&&&&*&STL&map&default&sort&order&by&key&&&&&&*&&&&&&*&STL&map&默认按照key升序排列&&&&&&*&这里要注意如果使用的map必须按key升序排列&&&&&&*&&&&&&*/&&&&&&string&buildContent(const&StringMap&&contentPairs);&&&&&&string&analyzeResponse(const&string&&responseStr);&&&&public:&&&&&&static&string&base64Encode(const&unsigned&char&*bytes,&int&len);&&&&&&static&bool&base64Decode(const&string&&str,&unsigned&char&*bytes,&int&&len);&&&&&&&&static&string&rsaSign(const&string&&content,&const&string&&key);&&&&&&static&bool&rsaVerify(const&string&&content,&const&string&&sign,&const&string&&key);&&&&public:&&&&&&string&getAppId();&&&&&&&&string&getSignType();&&&&&&&&string&getVersion();&&&&&&&&string&getCharset();&&&&&&&&string&getUrl();&&&&&&&&string&getAlipayPublicKey();&&&&};&&&&#endif&//&OPENAPICLIENT_H&&
#include&"openapi_client.h"&&&&&&const&string&OpenapiClient::default_charset&&&&&&=&"utf-8";&&const&string&OpenapiClient::default_url&&&&&&&&&&=&"/gateway.do";&&const&string&OpenapiClient::default_sign_type&&&&=&"RSA";&&const&string&OpenapiClient::default_version&&&&&&=&"2.0";&&&&const&string&OpenapiClient::KEY_APP_ID&&&&&&&&&&&=&"app_id";&&const&string&OpenapiClient::KEY_METHOD&&&&&&&&&&&=&"method";&&const&string&OpenapiClient::KEY_CHARSET&&&&&&&&&&=&"charset";&&const&string&OpenapiClient::KEY_SIGN_TYPE&&&&&&&&=&"sign_type";&&const&string&OpenapiClient::KEY_SIGN&&&&&&&&&&&&&=&"sign";&&const&string&OpenapiClient::KEY_TIMESTAMP&&&&&&&&=&"timestamp";&&const&string&OpenapiClient::KEY_VERSION&&&&&&&&&&=&"version";&&const&string&OpenapiClient::KEY_BIZ_CONTENT&&&&&&=&"biz_content";&&&&&&OpenapiClient::OpenapiClient(const&string&&appId,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&const&string&&privateKey,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&const&string&&url,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&const&string&&charset,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&const&string&&alipayPublicKey)&&&&&&:&appId(appId),&&&&&&&&privateKey(privateKey),&&&&&&&&signType(default_sign_type),&&&&&&&&version(default_version),&&&&&&&&url(url),&&&&&&&&charset(charset),&&&&&&&&alipayPublicKey(alipayPublicKey)&{&&&&}&&&&JsonMap&OpenapiClient::invoke(const&string&&method,&const&JsonMap&&contentMap,&const&StringMap&&extendParamMap)&{&&&&&&&&string&content&=&JsonUtil::objectToString(JsonType(contentMap));&&&&&&string&responseContent&=&invoke(method,&content,&extendParamMap);&&&&&&JsonType&jsonObj&=&JsonUtil::stringToObject(responseContent);&&&&&&return&jsonObj.toMap();&&}&&&&string&OpenapiClient::invoke(const&string&&method,&const&string&&content,&const&StringMap&&extendParamMap)&{&&&&&&&&time_t&t&=&time(0);&&&&&&char&tmp[64];&&&&&&strftime(tmp,&sizeof(tmp),&"%Y-%m-%d&%X",&localtime(&t));&&&&&&&&StringMap&requestP&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_APP_ID,&appId));&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_BIZ_CONTENT,&content));&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_CHARSET,&charset));&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_METHOD,&method));&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_SIGN_TYPE,&signType));&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_TIMESTAMP,&tmp));&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_VERSION,&version));&&&&&&&&/**&追加外部传入的网关的补充参数,如notify_url等&**/&&&&&&for&(StringMap::const_iterator&iter&=&extendParamMap.begin();&iter&!=&extendParamMap.end();&++iter)&{&&&&&&&&&&requestPairs.insert(StringMap::value_type(iter-&first,&iter-&second));&&&&&&}&&&&&&&&string&wholeContent&=&buildContent(requestPairs);&&&&&&string&sign&=&OpenapiClient::rsaSign(wholeContent,&privateKey);&&&&&&requestPairs.insert(StringMap::value_type(OpenapiClient::KEY_SIGN,&sign));&&&&&&&&wholeContent&=&buildContent(requestPairs);&&&&&&DebugLog("Request:%s",&wholeContent.c_str());&&&&&&&&HttpClient&httpC&&&&&&string&responseStr&=&httpClient.sendSyncRequest(url,&requestPairs);&&&&&&&&DebugLog("Response:%s",&responseStr.c_str());&&&&&&&&string&responseContent&=&analyzeResponse(responseStr);&&&&&&return&responseC&&}&&&&/**&&*&&*&STL&map&default&sort&order&by&key&&*&&*&STL&map&默认按照key升序排列&&*&这里要注意如果使用的map必须按key升序排列&&*&&*/&&string&OpenapiClient::buildContent(const&StringMap&&contentPairs)&{&&&&&&&&string&&&&&&&for&(StringMap::const_iterator&iter&=&contentPairs.begin();&&&&&&&&&&&iter&!=&contentPairs.end();&++iter)&{&&&&&&&&&&if&(!content.empty())&{&&&&&&&&&&&&&&content.push_back('&');&&&&&&&&&&}&&&&&&&&&&content.append(iter-&first);&&&&&&&&&&content.push_back('=');&&&&&&&&&&content.append(iter-&second);&&&&&&}&&&&&&return&&&}&&&&string&OpenapiClient::analyzeResponse(const&string&&responseStr)&{&&&&&&&&JsonType&responseObj&=&JsonUtil::stringToObject(responseStr);&&&&&&JsonMap&responseMap&=&responseObj.toMap();&&&&&&//获取返回报文中的alipay_xxx_xxx_response的内容;&&&&&&int&beg&=&responseStr.find("_response\"");&&&&&&int&end&=&responseStr.rfind("\"sign\"");&&&&&&if&(beg&&&0&||&end&&&0)&{&&&&&&&&&&return&string();&&&&&&}&&&&&&beg&=&responseStr.find('{',&beg);&&&&&&end&=&responseStr.rfind('}',&end);&&&&&&//注意此处将map转为json之后的结果需要与支付宝返回报文中原格式与排序一致;&&&&&&//排序规则是节点中的各个json节点key首字母做字典排序;&&&&&&//Response的Json值内容需要包含首尾的“{”和“}”两个尖括号,双引号也需要参与验签;&&&&&&//如果字符串中包含“http://”的正斜杠,需要先将正斜杠做转义,默认打印出来的字符串是已经做过转义的;&&&&&&//此处转换之后的json字符串默认为"Compact"模式,即紧凑模式,不要有空格与换行;&&&&&&string&responseContent&=&responseStr.substr(beg,&end&-&beg&+&1);&&&&&&&&DebugLog("ResponseContent:%s",&responseContent.c_str());&&&&&&&&//此处为校验支付宝返回报文中的签名;&&&&&&//如果支付宝公钥为空,则默认跳过该步骤,不校验签名;&&&&&&//如果支付宝公钥不为空,则认为需要校验签名;&&&&&&if&(!alipayPublicKey.empty())&{&&&&&&&&&&&&DebugLog("AlipayPublicKey:%s",&alipayPublicKey.c_str());&&&&&&&&&&&&JsonMap::const_iterator&iter&=&responseMap.find(OpenapiClient::KEY_SIGN);&&&&&&&&&&if&(iter&==&responseMap.end())&{&&&&&&&&&&&&&&DebugLog("Cannot&get&Sign&from&response,&Verify&Failed");&&&&&&&&&&&&&&return&string();&&&&&&&&&&}&&&&&&&&&&//获取返回报文中的&&&&&&&&&&string&responseSign&=&iter-&second.toString();&&&&&&&&&&&&DebugLog("ResponseSign:%s",&responseSign.c_str());&&&&&&&&&&&&//调用验签方法;&&&&&&&&&&bool&verifyResult&=&OpenapiClient::rsaVerify(responseContent,&responseSign,&alipayPublicKey);&&&&&&&&&&&&if&(!verifyResult)&{&&&&&&&&&&&&&&DebugLog("Verify&Failed");&&&&&&&&&&&&&&return&string();&&&&&&&&&&}&&&&&&&&&&DebugLog("Verify&Success");&&&&&&}&else&{&&&&&&&&&&DebugLog("AlipayPublicKey&is&empty,&Skip&the&Verify");&&&&&&}&&&&&&&&return&responseC&&}&&&&string&OpenapiClient::rsaSign(const&string&&content,&const&string&&key)&{&&&&&&&&string&signed_&&&&&&const&char&*key_cstr&=&key.c_str();&&&&&&int&key_len&=&strlen(key_cstr);&&&&&&BIO&*p_key_bio&=&BIO_new_mem_buf((void&*)key_cstr,&key_len);&&&&&&RSA&*p_rsa&=&PEM_read_bio_RSAPrivateKey(p_key_bio,&NULL,&NULL,&NULL);&&&&&&&&if&(p_rsa&!=&NULL)&{&&&&&&&&&&&&const&char&*cstr&=&content.c_str();&&&&&&&&&&unsigned&char&hash[SHA_DIGEST_LENGTH]&=&{0};&&&&&&&&&&SHA1((unsigned&char&*)cstr,&strlen(cstr),&hash);&&&&&&&&&&unsigned&char&sign[XRSA_KEY_BITS&/&8]&=&{0};&&&&&&&&&&unsigned&int&sign_len&=&sizeof(sign);&&&&&&&&&&int&r&=&RSA_sign(NID_sha1,&hash,&SHA_DIGEST_LENGTH,&sign,&&sign_len,&p_rsa);&&&&&&&&&&&&if&(0&!=&r&&&&sizeof(sign)&==&sign_len)&{&&&&&&&&&&&&&&signed_str&=&base64Encode(sign,&sign_len);&&&&&&&&&&}&&&&&&}&&&&&&&&RSA_free(p_rsa);&&&&&&BIO_free(p_key_bio);&&&&&&return&signed_&&}&&&&bool&OpenapiClient::rsaVerify(const&string&&content,&const&string&&sign,&const&string&&key)&{&&&&&&&&bool&result&=&false;&&&&&&const&char&*key_cstr&=&key.c_str();&&&&&&int&key_len&=&strlen(key_cstr);&&&&&&BIO&*p_key_bio&=&BIO_new_mem_buf((void&*)key_cstr,&key_len);&&&&&&RSA&*p_rsa&=&PEM_read_bio_RSA_PUBKEY(p_key_bio,&NULL,&NULL,&NULL);&&&&&&&&if&(p_rsa&!=&NULL)&{&&&&&&&&&&const&char&*cstr&=&content.c_str();&&&&&&&&&&unsigned&char&hash[SHA_DIGEST_LENGTH]&=&{0};&&&&&&&&&&SHA1((unsigned&char&*)cstr,&strlen(cstr),&hash);&&&&&&&&&&unsigned&char&sign_cstr[XRSA_KEY_BITS&/&8]&=&{0};&&&&&&&&&&int&len&=&XRSA_KEY_BITS&/&8;&&&&&&&&&&base64Decode(sign,&sign_cstr,&len);&&&&&&&&&&unsigned&int&sign_len&=&XRSA_KEY_BITS&/&8;&&&&&&&&&&int&r&=&RSA_verify(NID_sha1,&hash,&SHA_DIGEST_LENGTH,&(unsigned&char&*)sign_cstr,&sign_len,&p_rsa);&&&&&&&&&&&&if&(r&&&0)&{&&&&&&&&&&&&&&result&=&true;&&&&&&&&&&}&&&&&&}&&&&&&&&RSA_free(p_rsa);&&&&&&BIO_free(p_key_bio);&&&&&&return&&&}&&&&string&OpenapiClient::base64Encode(const&unsigned&char&*bytes,&int&len)&{&&&&&&&&BIO&*bmem&=&NULL;&&&&&&BIO&*b64&=&NULL;&&&&&&BUF_MEM&*bptr&=&NULL;&&&&&&&&b64&=&BIO_new(BIO_f_base64());&&&&&&BIO_set_flags(b64,&BIO_FLAGS_BASE64_NO_NL);&&&&&&bmem&=&BIO_new(BIO_s_mem());&&&&&&b64&=&BIO_push(b64,&bmem);&&&&&&BIO_write(b64,&bytes,&len);&&&&&&BIO_flush(b64);&&&&&&BIO_get_mem_ptr(b64,&&bptr);&&&&&&&&string&str&=&string(bptr-&data,&bptr-&length);&&&&&&BIO_free_all(b64);&&&&&&return&&&}&&&&bool&OpenapiClient::base64Decode(const&string&&str,&unsigned&char&*bytes,&int&&len)&{&&&&&&&&const&char&*cstr&=&str.c_str();&&&&&&BIO&*bmem&=&NULL;&&&&&&BIO&*b64&=&NULL;&&&&&&&&b64&=&BIO_new(BIO_f_base64());&&&&&&BIO_set_flags(b64,&BIO_FLAGS_BASE64_NO_NL);&&&&&&bmem&=&BIO_new_mem_buf((void&*)cstr,&strlen(cstr));&&&&&&b64&=&BIO_push(b64,&bmem);&&&&&&len&=&BIO_read(b64,&bytes,&len);&&&&&&&&BIO_free_all(b64);&&&&&&return&len&&&0;&&}&&&&&&string&OpenapiClient::getAppId()&{&&&&&&return&appId;&&}&&&&string&OpenapiClient::getSignType()&{&&&&&&return&signT&&}&&&&string&OpenapiClient::getVersion()&{&&&&&&return&&&}&&&&string&OpenapiClient::getCharset()&{&&&&&&return&&&}&&&&string&OpenapiClient::getUrl()&{&&&&&&return&&&}&&&&string&OpenapiClient::getAlipayPublicKey()&{&&&&&&return&alipayPublicK&&}&
馆藏&22546
TA的最新馆藏}

我要回帖

更多关于 php 中文转义 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信