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

yipeiwu_com5年前Python基础

本文实例为大家分享了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设计】。

相关文章

pytorch GAN生成对抗网络实例

pytorch GAN生成对抗网络实例

我就废话不多说了,直接上代码吧! import torch import torch.nn as nn from torch.autograd import Variable imp...

Ubuntu安装Jupyter Notebook教程

Ubuntu安装Jupyter Notebook教程

一.Jupyter介绍 Jupyter Notebook是一个交互式笔记本,支持运行40多种编程语言。Jupyter Notebook 的本质是一个 Web 应用程序,便于创建和共享文学...

python学习之面向对象【入门初级篇】

python学习之面向对象【入门初级篇】

前言 最近在学习Python的面向对象编程,以前是没有接触过其它的面向对象编程的语言,因此学习这一部分是相当带劲的,这里也总结一下。 概述 python支持多种编程范式:面向过程、...

跟老齐学Python之Import 模块

跟老齐学Python之Import 模块

认识模块 对于模块,在前面的一些举例中,已经涉及到了,比如曾经有过:import random (获取随机数模块)。为了能够对模块有一个清晰的了解,首先要看看什么模块,这里选取官方文档中...

浅谈django rest jwt vue 跨域问题

django rest framework 使用 router 注册url时,访问接口 包302错误 可能是因为请求url 写法有问题, 如请求 /api/login/ 报302 ,需要...