python模拟支付宝支付(一篇文章搞定Python快速开发支付宝支付的方法)
python模拟支付宝支付(一篇文章搞定Python快速开发支付宝支付的方法)#python##支付##支付宝##开发##编程#文件名:alipay_public_2048.txt 注意秘钥需要以 -----BEGIN PUBLIC KEY----- 支付宝公钥 -----END PUBLIC KEY----- 文件名:app_private_2048.txt -----BEGIN RSA PRIVATE KEY----- 应用私钥 -----END RSA PRIVATE KEY-----目录结构运行中可能报的错【Not a valid PEM post boundary】 导致其中的一种原因为在生成应用公钥与应用私钥后使用的记事本打开的,未将记事本的自动换行关闭,导致存在不可见的换行符,故在使用记事本打开后请关闭掉自动换行。from django.shortcuts import render redirect HttpResponse from
前期准备(安装依赖模块)pip3 install pycryptodome
去蚂蚁金服开放平台申请
蚂蚁金服开放平台网址
https://open.alipay.com/platform/home.htm
注册流程(注意正式支付开发与其一样只不过获取就不是沙箱的各项信息了)
登录蚂蚁金服开放平台(与支付宝同意账号)---》注册成为开发者---》填写各种信息---》成功后点击顶部开发者中心----》启动沙箱环境---》沙箱应用-----》生成RSA2(SHA256)密钥(推荐)【查看生成方法文档】-----填写完毕
由于蚂蚁金服未提供Python开发的SDK(需要去github上下载其方法)
from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from urllib.parse import urlparse parse_qs
from base64 import decodebytes encodebytes
import json
class AliPay(object):
"""
支付宝支付接口(PC端支付接口)
"""
def __init__(self appid app_notify_url app_private_key_path
alipay_public_key_path return_url debug=False):
self.appid = appid
self.app_notify_url = app_notify_url
self.app_private_key_path = app_private_key_path
self.app_private_key = None
self.return_url = return_url
with open(self.app_private_key_path) as fp:
self.app_private_key = RSA.importKey(fp.read())
self.alipay_public_key_path = alipay_public_key_path
with open(self.alipay_public_key_path) as fp:
self.alipay_public_key = RSA.importKey(fp.read())
# 注意如果debug如果不是True将不是沙箱环境【标识非正式环境url中含有dev字样】
if debug is True:
self.__gateway = "https://openapi.alipaydev.com/gateway.do"
else:
self.__gateway = "https://openapi.alipay.com/gateway.do"
def direct_pay(self subject out_trade_no total_amount return_url=None **kwargs):
biz_content = {
"subject": subject
"out_trade_no": out_trade_no
"total_amount": total_amount
"product_code": "FAST_INSTANT_TRADE_PAY"
# "qr_pay_mode":4
}
biz_content.update(kwargs)
data = self.build_body("alipay.trade.page.pay" biz_content self.return_url)
return self.sign_data(data)
def build_body(self method biz_content return_url=None):
data = {
"app_id": self.appid
"method": method
"charset": "utf-8"
"sign_type": "RSA2"
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
"version": "1.0"
"biz_content": biz_content
}
if return_url is not None:
data["notify_url"] = self.app_notify_url
data["return_url"] = self.return_url
return data
def sign_data(self data):
data.pop("sign" None)
# 排序后的字符串
unsigned_items = self.ordered_data(data)
unsigned_string = "&".join("{0}={1}".format(k v) for k v in unsigned_items)
sign = self.sign(unsigned_string.encode("utf-8"))
# ordered_items = self.ordered_data(data)
quoted_string = "&".join("{0}={1}".format(k quote_plus(v)) for k v in unsigned_items)
# 获得最终的订单信息字符串
signed_string = quoted_string "&sign=" quote_plus(sign)
return signed_string
def ordered_data(self data):
complex_keys = []
for key value in data.items():
if isinstance(value dict):
complex_keys.append(key)
# 将字典类型的数据dump出来
for key in complex_keys:
data[key] = json.dumps(data[key] separators=(' ' ':'))
return sorted([(k v) for k v in data.items()])
def sign(self unsigned_string):
# 开始计算签名
key = self.app_private_key
signer = PKCS1_v1_5.new(key)
signature = signer.sign(SHA256.new(unsigned_string))
# base64 编码,转换为unicode表示并移除回车
sign = encodebytes(signature).decode("utf8").replace("\n" "")
return sign
def _verify(self raw_content signature):
# 开始计算签名
key = self.alipay_public_key
signer = PKCS1_v1_5.new(key)
digest = SHA256.new()
digest.update(raw_content.encode("utf8"))
if signer.verify(digest decodebytes(signature.encode("utf8"))):
return True
return False
def verify(self data signature):
if "sign_type" in data:
sign_type = data.pop("sign_type")
# 排序后的字符串
unsigned_items = self.ordered_data(data)
message = "&".join(u"{}={}".format(k v) for k v in unsigned_items)
return self._verify(message signature)
github下载下来的源码(pay.py)
其它部分源码
templates/index.html
templates/index.html<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width initial-scale=1">
</head>
<body>
<form method="post">
{% csrf_token %}
<input type="text" name="price" placeholder="请输入要支付的金额">
<input type="submit" value="支付">
</form>
</body>
</html>
应用pay/views.py
from django.shortcuts import render redirect HttpResponse
from django.views.decorators.csrf import csrf_exempt
from utils.pay import AliPay
import time
from django.conf import settings
def aliPay():
obj = AliPay(
appid=settings.APPID
app_notify_url=settings.NOTIFY_URL # 如果支付成功,支付宝会向这个地址发送POST请求(校验是否支付已经完成)
return_url=settings.RETURN_URL # 如果支付成功,重定向回到你的网站的地址。
alipay_public_key_path=settings.PUB_KEY_PATH # 支付宝公钥
app_private_key_path=settings.PRI_KEY_PATH # 应用私钥
debug=True # 默认False
)
return obj
def index(request):
if request.method == 'GET':
return render(request 'index.html')
alipay = aliPay()
# 对购买的数据进行加密
money = float(request.POST.get('price'))
out_trade_no = "x2" str(time.time())
# 1. 在数据库创建一条数据:状态(待支付)
query_params = alipay.direct_pay(
subject="【全地形代步工具而二点零】" # 商品简单描述
out_trade_no= out_trade_no # 商户订单号
total_amount=money # 交易金额(单位: 元 保留俩位小数)
)
pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
return redirect(pay_url)
def pay_result(request):
"""
支付完成后,跳转回的地址
:param request:
:return:
"""
params = request.GET.dict()
sign = params.pop('sign' None)
alipay = aliPay()
status = alipay.verify(params sign)
if status:
return HttpResponse('支付成功')
return HttpResponse('支付失败')
@csrf_exempt
def update_order(request):
"""
支付成功后,支付宝向该地址发送的POST请求(用于修改订单状态)
:param request:
:return:
"""
if request.method == 'POST':
from urllib.parse import parse_qs
body_str = request.body.decode('utf-8')
post_data = parse_qs(body_str)
post_dict = {}
for k v in post_data.items():
post_dict[k] = v[0]
alipay = aliPay()
sign = post_dict.pop('sign' None)
status = alipay.verify(post_dict sign)
if status:
# 修改订单状态
out_trade_no = post_dict.get('out_trade_no')
print(out_trade_no)
# 2. 根据订单号将数据库中的数据进行更新
return HttpResponse('支付成功')
else:
return HttpResponse('支付失败')
return HttpResponse('')
项目urls.py
from django.conf.urls import url
from django.contrib import admin
from pay import views
urlpatterns = [
url(r'^admin/' admin.site.urls)
url(r'^index/' views.index)
url(r'^pay_result/' views.pay_result)
url(r'^update_order/' views.update_order)
]
settings.py中新增
# 支付相关配置
APPID = "申请下来的APPID "
# 如果支付成功,支付宝会向这个地址发送POST请求(校验是否支付已经完成)
NOTIFY_URL = "http://xxx.xxx.xx.xx:xx/update_order/"
# 如果支付成功,重定向回到你的网站的地址。
RETURN_URL = "http://xxx.xxx.xx.xx:xxxx/pay_result/"
PRI_KEY_PATH = "keys/app_private_2048.txt" # # 支付宝公钥
PUB_KEY_PATH = "keys/alipay_public_2048.txt" # 应用私钥
新建key目录下放的文件
文件名:alipay_public_2048.txt
注意秘钥需要以
-----BEGIN PUBLIC KEY-----
支付宝公钥
-----END PUBLIC KEY-----
文件名:app_private_2048.txt
-----BEGIN RSA PRIVATE KEY-----
应用私钥
-----END RSA PRIVATE KEY-----
目录结构
导致其中的一种原因为在生成应用公钥与应用私钥后使用的记事本打开的,未将记事本的自动换行关闭,导致存在不可见的换行符,故在使用记事本打开后请关闭掉自动换行。
#python##支付##支付宝##开发##编程#