python实现银联支付和支付宝支付接入


Posted in Python onMay 07, 2019

本文实例为大家分享了python银联支付和支付宝支付接入的具体代码,供大家参考,具体内容如下

前置条件:需要安装Python的OpenSSL模块,我使用的版本是16.1.0,可以使用pip install pyopenssl来安装

一、支付宝支付

1. 使用RSA公钥加密系统进行签名和签名验证,需要自己生成一个RSA私钥和对应的一个RSA公钥(在Linux下可以使用ssh-keygen命令来生成),公钥需要上传至支付宝,供支付宝对开发者发送的请求做签名验证使用;而同时支付宝会提供一个RSA公钥给开发者,开发者使用这个公钥来验证支付宝的回调请求的合法性。

2. 整个接入过程最核心的工作就是构建一个合法的请求报文,这个可以参考支付宝的相关文档;其次是对请求报文的内容进行RSA签名,并将签名随请求报文一并发送。

核心的签名和报文构建代码如下:

import OpenSSL
import json
import time
import urllib
import base64
 
from django.conf import settings
 
def build_sign(param_map, sign_type="RSA"):
 '''
 Doc: https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105351&docType=1
 '''
 # 将筛选的参数按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。
 sort_param = sorted([(key, unicode(value, settings.ALIPAY_CHARSET).encode(settings.ALIPAY_CHARSET)) for key, value in param_map.iteritems()], key=lambda x: x[0])
 # 将排序后的参数与其对应值,组合成“参数=参数值”的格式,并且把这些参数用&字符连接起来,此时生成的字符串为待签名字符串。SDK中已封装签名方法,开发者可直接调用,详见SDK说明。
 # 如自己开发,则需将待签名字符串和私钥放入SHA1 RSA算法中得出签名(sign)的值。
 content = '&'.join(['='.join(x) for x in sort_param])
 return base64.encodestring(OpenSSL.crypto.sign(settings.ALIPAY_APP_PRIVATE_KEY_OBJ, content, 'sha1'))
 
def build_params(out_trade_no, subject, body, total_amount):
 '''
 Doc:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.MVkRGo&treeId=193&articleId=105465&docType=1
 将参数按照支付宝规定组织并签名之后,返回
 '''
 params = {}
 # 获取配置文件
 params['app_id']   = settings.ALIPAY_APPID
 params['method']   = settings.ALIPAY_METHOD
 params['format']   = settings.ALIPAY_FORMAT
 params['charset']   = settings.ALIPAY_CHARSET
 params['sign_type']   = settings.ALIPAY_SIGN_TYPE
 params['sign_type']   = settings.ALIPAY_SIGN_TYPE
 params['timestamp']   = time.strftime('%Y-%m-%d %H:%M:%S')
 params['version']   = settings.ALIPAY_VERSION
 params['notify_url']  = settings.ALIPAY_NOTIFY_URL
 
 # 业务参数
 params['biz_content'] = {}
 params['biz_content']['body']    = body   # 订单描述、订单详细、订单备注,显示在支付宝收银台里的“商品描述”里
 params['biz_content']['subject']   = subject  # 商品的标题/交易标题/订单标题/订单关键字等。
 params['biz_content']['out_trade_no']  = out_trade_no # 商户网站唯一订单号 
 params['biz_content']['total_amount']  = '%.2f' % (float(total_amount) / 100) # 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 
 params['biz_content']['product_code']  = settings.ALIPAY_APP_PRODUCT_CODE
 params['biz_content']      = json.dumps(params['biz_content'], separators=(',', ':'))
 
 params['sign'] = build_sign(params)
 
 return urllib.urlencode(params)
 
def check_sign(message, sign):
 '''Doc: https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.dDRpeK&treeId=204&articleId=105301&docType=1'''
 try:
  OpenSSL.crypto.verify(settings.ALIPAY_PUBLIC_KEY_OBJ, sign, message, 'SHA1')
  return True
 except Exception as _:
  return False

在读取支付宝公钥的时候,由于OpenSSL模块使用的是X509格式的证书,所以需要做以下的处理(用于对发起支付报文进行签名):

# 支付宝开发者公钥(支付宝生成)
ALIPAY_PUBLIC_KEY = os.path.join(BASE_DIR, 'utils/paycenter/alipay/certs/alipay_public_key')
_ALIPAY_PUBLIC_KEY_OBJ_PUB = OpenSSL.crypto.load_publickey(OpenSSL.crypto.FILETYPE_PEM, open(ALIPAY_PUBLIC_KEY).read())
_ALIPAY_PUBLIC_KEY_OBJ_X509 = OpenSSL.crypto.X509()
_ALIPAY_PUBLIC_KEY_OBJ_X509.set_pubkey(_ALIPAY_PUBLIC_KEY_OBJ_PUB)
ALIPAY_PUBLIC_KEY_OBJ = _ALIPAY_PUBLIC_KEY_OBJ_X509

载入开发者生成的私钥的代码为(用于验证支付宝回调请求的合法性):

# 支付宝开发者应用私钥(接入方生成)
ALIPAY_APP_PRIVATE_KEY = os.path.join(BASE_DIR, 'utils/paycenter/alipay/certs/alipay_app_private_key')
ALIPAY_APP_PRIVATE_KEY_OBJ = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open(ALIPAY_APP_PRIVATE_KEY).read(), None)

二、银联支付

1. 银联支付(网关支付)与支付宝支付基本上遵循了同样的流程,但是在发起支付的请求报文和签名、验证签名等方面存在细微的差别,特别是在签名和验证签名时,支付宝是直接对报文内容进行了RSA加密,但是银联却是首先对签名内容取SHA1的摘要,继而对此摘要做RSA加密,这点在build_sign函数中就可以很明显地看出来。

2. 银联支付的证书文件申请流程比较繁琐,并且其格式也和支付宝使用的不同,我们申请到的私钥证书文件是以PKCS12的格式保存的,并且需要注意在从指定位置下载此证书后,导出证书时一定要将密码设置为6位的整数数字,之后,还需要将PKCS12格式的证书文件上传至指定位置并启用才可以正常使用此证书。

核心的签名和报文构建代码如下:

import time
import hashlib
import urllib, urllib2
import base64
import OpenSSL
from django.conf import settings
 
def build_sign(param_map, sign_type="RSA"):
 '''构建签名'''
 # 将筛选的参数按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。
 sort_param = sorted([(key, unicode(value, settings.UNIONPAY_ENCODING).encode(settings.UNIONPAY_ENCODING)) for key, value in param_map.iteritems()], key=lambda x: x[0])
 content = '&'.join(['='.join(x) for x in sort_param])
 message = hashlib.sha1(content).hexdigest()
 return base64.b64encode(OpenSSL.crypto.sign(settings.UNIONPAY_PRIVATE_KEY_OBJ, message, 'sha1'))
 
def build_params(out_trade_no, total_amount):
 params = {}
 # 获取配置信息
 params['accType'] = settings.UNIONPAY_ACC_TYPE
 params['accessType'] = settings.UNIONPAY_ACCESS_TYPE
 params['backUrl'] = settings.UNIONPAY_BACK_URL
 params['frontUrl'] = settings.UNIONPAY_FRONT_URL
 params['bizType'] = settings.UNIONPAY_BIZ_TYPE
 params['certId'] = settings.UNIONPAY_CERT_ID
 params['channelType'] = settings.UNIONPAY_CHANNEL_TYPE
 params['currencyCode'] = settings.UNIONPAY_CURRENCY_CODE
 params['encoding'] = settings.UNIONPAY_ENCODING
 params['merId'] = settings.UNIONPAY_MER_ID
 params['signMethod'] = settings.UNIONPAY_SIGN_METHOD
 params['txnType'] = settings.UNIONPAY_TXN_TYPE
 params['txnSubType'] = settings.UNIONPAY_TXN_SUBTYPE
 params['version'] = settings.UNIONPAY_VERSION
 
 params['orderId'] = out_trade_no
 params['txnAmt'] = '%d' % int(total_amount) # 单位为分
 params['txnTime'] = time.strftime('%Y%m%d%H%M%S') # 
 
 params['signature'] = build_sign(params)
#  return params
 return urllib.urlencode(params)
 
def check_sign(message, sign):
 try:
  OpenSSL.crypto.verify(settings.UNIONPAY_PUBLIC_KEY_OBJ, sign, message, 'SHA1')
  return True
 except Exception as _:
  return False

商户私钥证书的载入方法(用户对发起支付的报文进行签名):

# 商户私钥证书
UNIONPAY_APP_PRIVATE_KEY_CERT = os.path.join(UNIONPAY_CERTS_PATH, UNIONPAY_APP_PRIVATE_KEY_CERT_FILENAME) # PKCS12 format
UNIONPAY_PRIVATE_KEYSTORE = OpenSSL.crypto.load_pkcs12(open(UNIONPAY_APP_PRIVATE_KEY_CERT).read(), UNIONPAY_APP_PRIVATE_KEY_CERT_PASSWORD)
UNIONPAY_PRIVATE_KEY_OBJ = UNIONPAY_PRIVATE_KEYSTORE.get_privatekey()

银联公钥证书的载入方法(用于验证银联回调的合法性):

# 银联公钥证书
UNIONPAY_PUBLIC_KEY_CERT = os.path.join(UNIONPAY_CERTS_PATH, UNIONPAY_PUBLIC_KEY_CERT_FILENAME)
UNIONPAY_PUBLIC_KEY_OBJ = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, open(UNIONPAY_PUBLIC_KEY_CERT).read())

后记:银联支付接入过程中,参考过其官方提供的一个Python实现的接口,里面同时使用了python的rsa模块和OpenSSL模块,并且对证书的格式做了各种处理,但是这些似乎是没有什么必要,只使用OpenSSL模块就完全可以完成相关的工作。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python格式化css文件的方法
Mar 10 Python
python抽取指定url页面的title方法
May 11 Python
python爬取微信公众号文章
Aug 31 Python
解决Pycharm出现的部分快捷键无效问题
Oct 22 Python
一文带你了解Python中的字符串是什么
Nov 20 Python
python 阶乘累加和的实例
Feb 01 Python
Python中dict和set的用法讲解
Mar 28 Python
pycharm运行程序时看不到任何结果显示的解决
Feb 21 Python
python 实现全球IP归属地查询工具
Dec 18 Python
如何用Python编写一个电子考勤系统
Feb 08 Python
Python的flask接收前台的ajax的post数据和get数据的方法
Apr 12 Python
Python中的变量与常量
Nov 11 Python
详解Python 爬取13个旅游城市,告诉你五一大家最爱去哪玩?
May 07 #Python
Scrapy-Redis结合POST请求获取数据的方法示例
May 07 #Python
Python数据类型之Set集合实例详解
May 07 #Python
Python数据类型之Dict字典实例详解
May 07 #Python
Python分支语句与循环语句应用实例分析
May 07 #Python
Python正则表达式实现简易计算器功能示例
May 07 #Python
深入浅析Python 中 is 语法带来的误解
May 07 #Python
You might like
1982年日本摄影师镜头下的中国孩子 那无忧无虑的童年
2020/03/12 杂记
十天学会php之第二天
2006/10/09 PHP
PHP手机号码归属地查询代码(API接口/mysql)
2012/09/04 PHP
PHP生成指定长度随机数最简洁的方法
2014/07/14 PHP
PHP之正则表达式捕获组与非捕获组(详解)
2015/07/29 PHP
thinkphp实现图片上传功能
2016/01/13 PHP
jQuery 连续列表实现代码
2009/12/21 Javascript
JavaScript中使用构造函数实现继承的代码
2010/08/12 Javascript
JS 实现导航栏悬停效果(续)
2013/09/24 Javascript
js 一个关于图片onload加载的事
2013/11/10 Javascript
javascript操作字符串的原生方法
2014/12/22 Javascript
Jquery on方法绑定事件后执行多次的解决方法
2016/06/02 Javascript
jQuery弹出遮罩层效果完整示例
2016/09/13 Javascript
Bootstrap框架结合jQuery仿百度换肤功能实例解析
2016/09/17 Javascript
判断数组的最佳方法(推荐)
2016/10/11 Javascript
简单谈谈React中的路由系统
2017/07/25 Javascript
微信小程序富文本渲染引擎的详解
2017/09/30 Javascript
nodejs实现日志读取、日志查找及日志刷新的方法分析
2019/05/20 NodeJs
vue2.0 实现富文本编辑器功能
2019/05/26 Javascript
怎么使用javascript深度拷贝一个数组
2019/06/06 Javascript
Python字符串拼接、截取及替换方法总结分析
2016/04/13 Python
Python深度优先算法生成迷宫
2018/01/22 Python
numpy中以文本的方式存储以及读取数据方法
2018/06/04 Python
Python Flask 搭建微信小程序后台详解
2019/05/06 Python
500行Python代码打造刷脸考勤系统
2019/06/03 Python
Python实现检测文件的MD5值来查找重复文件案例
2020/03/12 Python
Fox Racing官方网站:越野摩托车和山地自行车装备和服装
2019/12/23 全球购物
八年级语文教学反思
2014/02/11 职场文书
促销活动总结报告
2014/04/26 职场文书
小学校园之星事迹材料
2014/05/16 职场文书
医院安全生产月活动总结
2014/07/05 职场文书
家庭贫困证明范本(经典版)
2014/09/22 职场文书
2016年大学校运会广播稿件
2015/12/21 职场文书
《家》读后感:万惜拯救,冷暖自知
2019/09/25 职场文书
JavaScript 中for/of,for/in 的详细介绍
2021/11/17 Javascript
Python之matplotlib绘制折线图
2022/04/13 Python