支付宝支付功能
登录支付宝官网开发平台
下载SDK,
支付宝并没有python版本SDK
SDK有实例和代码。
没有python则自己写,或者去github找。
本次利用alipay这个组件。
先安装一个pycrytodome
pip3 install pycrytodome
如果真实环境,按上面这个来注册。
测试环境需要用沙箱环境。
向支付宝提交的这个链接,在测试模式有dev,生产环境删掉dev
公钥复制到支付宝里,私钥存到自己django文件中,自己文件的公钥是来自支付宝的公钥。
上面沙箱环境不需要配置。
下面是创建一个生产环境的应用。
以下为支持的功能
以上为真实生产需要的操作。
然后之后看pay项目代码。
以上为目录结构。
views.py:
1 from django.shortcuts import render, redirect, HttpResponse 2 from utils.pay import AliPay 3 import json 4 import time 5 6 7 def ali(): 8 # 商户app_id 9 app_id = "2016082600312402" #复制来自支付宝生成的id 10 # 服务器异步通知页面路径 需http: // 格式的完整路径,不能加?id = 123 这类自定义参数,必须外网可以正常访问 11 # 发post请求 12 notify_url = "http://www.xxxx.com:8009/page2/" #将这两个链接复制到支付宝中 13 14 # 页面跳转同步通知页面路径 需http: // 格式的完整路径,不能加?id = 123 这类自定义参数,必须外网可以正常访问 15 # 发get请求 16 return_url = "http://www.xxxx.com:8009/page2/" 17 # 商户私钥路径 18 merchant_private_key_path = "keys/pri" #设置公钥和私钥的地址,文件上下两行begin和end是必须的,公钥就放在第二行。 19 # 支付宝公钥路径 20 alipay_public_key_path = "keys/pub" 21 22 alipay = AliPay( 23 appid=app_id, 24 app_notify_url=notify_url, 25 return_url=return_url, 26 app_private_key_path=merchant_private_key_path, 27 alipay_public_key_path=alipay_public_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥 28 debug=True, # 默认False, 29 ) 30 return alipay 31 32 33 def page1(request): 34 if request.method == "GET": 35 return render(request, ‘page1.html‘) 36 else: 37 money = float(request.POST.get(‘money‘)) 38 alipay = ali() 39 # 生成支付的url 40 query_params = alipay.direct_pay( 41 subject="充气式减压玩具", # 商品简单描述 42 out_trade_no="x2" + str(time.time()), # 商户订单号 43 total_amount=money, # 交易金额(单位: 元 保留俩位小数) 44 ) 45 46 pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params) 47 #支付宝网关链接,去掉dev就是生产环境了。 48 return redirect(pay_url) 49 50 51 def page2(request): 52 alipay = ali() 53 if request.method == "POST": 54 # 检测是否支付成功 55 # 去请求体中获取所有返回的数据:状态/订单号 56 from urllib.parse import parse_qs 57 58 # request.body => 字节类型 59 # request.body.decode(‘utf-8‘) => 字符串类型 60 """ 61 {"k1":["v1"],"k2":["v1"]} 62 k1=[v1]&k2=[v2] 63 """ 64 body_str = request.body.decode(‘utf-8‘) 65 post_data = parse_qs(body_str) 66 # {k1:[v1,],k2:[v2,]} 67 68 # {k1:v1} 69 post_dict = {} 70 for k, v in post_data.items(): 71 post_dict[k] = v[0] 72 73 74 print(post_dict) 75 """ 76 {‘gmt_create‘: ‘2017-11-24 14:53:41‘, ‘charset‘: ‘utf-8‘, ‘gmt_payment‘: ‘2017-11-24 14:53:48‘, ‘notify_time‘: ‘2017-11-24 14:57:05‘, ‘subject‘: ‘充气式韩红‘, ‘sign‘: ‘YwkPI9BObXZyhq4LM8//MixPdsVDcZu4BGPjB0qnq2zQj0SutGVU0guneuONfBoTsj4XUMRlQsPTHvETerjvrudGdsFoA9ZxIp/FsZDNgqn9i20IPaNTXOtQGhy5QUetMO11Lo10lnK15VYhraHkQTohho2R4q2U6xR/N4SB1OovKlUQ5arbiknUxR+3cXmRi090db9aWSq4+wLuqhpVOhnDTY83yKD9Ky8KDC9dQDgh4p0Ut6c+PpD2sbabooJBrDnOHqmE02TIRiipULVrRcAAtB72NBgVBebd4VTtxSZTxGvlnS/VCRbpN8lSr5p1Ou72I2nFhfrCuqmGRILwqw==‘, ‘buyer_id‘: ‘2088102174924590‘, ‘invoice_amount‘: ‘666.00‘, ‘version‘: ‘1.0‘, ‘notify_id‘: ‘11aab5323df78d1b3dba3e5aaf7636dkjy‘, ‘fund_bill_list‘: ‘[{"amount":"666.00","fundChannel":"ALIPAYACCOUNT"}]‘, ‘notify_type‘: ‘trade_status_sync‘, ‘out_trade_no‘: ‘x21511506412.4733646‘, ‘total_amount‘: ‘666.00‘, ‘trade_status‘: ‘TRADE_SUCCESS‘, ‘trade_no‘: ‘2017112421001004590200343962‘, ‘auth_app_id‘: ‘2016082500309412‘, ‘receipt_amount‘: ‘666.00‘, ‘point_amount‘: ‘0.00‘, ‘app_id‘: ‘2016082500309412‘, ‘buyer_pay_amount‘: ‘666.00‘, ‘sign_type‘: ‘RSA2‘, ‘seller_id‘: ‘2088102172939262‘} 77 {‘stade_status‘: "trade_success",‘order‘:‘x2123123123123‘} 78 """ 79 sign = post_dict.pop(‘sign‘, None) 80 81 status = alipay.verify(post_dict, sign) 82 if status: 83 print(post_dict[‘stade_status‘]) 84 print(post_dict[‘out_trade_no‘]) 85 86 return HttpResponse(‘POST返回‘) 87 else: 88 # QueryDict = {‘k‘:[1],‘k1‘:[11,22,3]} 89 params = request.GET.dict() 90 sign = params.pop(‘sign‘, None) 91 status = alipay.verify(params, sign) 92 print(‘GET验证‘, status) 93 return HttpResponse(‘支付成功‘)
key目录
-----BEGIN RSA PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZWExNxlfrbIknZmVwzK8MNYkP8aW2XChMNUD/+Gi3OC/k417Q6C326nxlYQy3FbsjPBofVapbeHY2y0J3t3m1tIuH3MBDqAM2pt3mExl7xr8hBROfD71V39XplJhE2WRPN2BdJLiYVYXgTTkpXunA1zsbt0gdmQK2VoQj3cdy7EukUferBRwsxxIe0neCPWwq4DlQeHu94csA3smXZLyOt8fovd4ksiphQ3U5R/aNCxV0vY47zSWK+jYKQUzpbRidVeQX3nT9G6M8w7b5rlCB1VZQBLwiVfYCVNTxcTN8qI5NaMvbWeKrr0YNRTKKzn2qL4CKLuyDoCMXo+cyDnnFAgMBAAECggEAFqTobkH37wND1uMINpRcuHzrZsnaZgF8AVSbDRAoeM5VzxcRTdqiz1Lm2vkdhgWxlZ4xaopWUWlfh53tsuNevtusnd8V+PaMPylrfQkIYqj2SM5qmOve4g+MDeX5Z1Lu7IHsfEfTI6vlYtUo23KUEA9cjSqvTMYgTjb9VW9J1GMw5ccPkret2Emp1ThzaUzaetcGkSbllFuHSlwZ2fu6egxQPNZHDmyuH708cxW8FyB7ilR23cMqTXh4u5ytEEkt6mRb9xman8D7yaE7JEBlXSKCvZBDv73qsuEIHSI3tomLIB5l0kxV58gq90giTnPnwcwn4ylJypUMfTr8O6JFAQKBgQDvzrUOM0XrPhg8aP5suzvV/MhP3z+F4Fm7Z5eBS3HiGq1lYzqhzjCE9bZ7uXwYbvLvLf8lpWsubeU/dO0Ts6yfm4yr43sOsIpuBWoQK+MQIriDkeMeld4+adbEFFxyPSATdpEvz8pUqsWAdTAF5/kawQXtUiSbbPLHtDNf3Nc/MQKBgQDoBU4gcLnrzA7i8xSn4YqEpR0brQGE0L4QRx4IVLzYvKv3swJqM9+B3ICrgJ1B9zr+IPN0DTu4ZunWyVDmFxLfwMNuqg5zmHeUf+XYY3toDm7co1LoFXp5oMA9n6fYKlcTvz1d7mK0sQ0P2adWFLp1cX0yEEEFNVCPV8L1KcfG1QKBgHT8TwEdbDeFdEdMJQogEOGkTogAbbm+p6evso5Fosndn8c9MCYtMyg5wgr7gplczrB1rOnNl8rvm41oWhtpZIX8WRSlhau13eIsTACVmFCPz5mHutd53xBti3LeR/cG8LXt+ofrg1XodS7kfEf1UWWG0oBiuS8FaC6aLxHN50eRAoGBAJv4tQRpwxIYIwm9ju0sWQnCVUb9jj/Sc3JN3IqNLEYmzxO8aqsqI94QdQ7VbuGhaS7cx8wD+VmFFT0mKBQE/tMbqkUCXKSoofpZ4BEPDy5sRWpdAKaziZmpzpGeeh5+l/rWXFKApioBu14kWrErTg0VNawp8QunQ3iY7p4QcBPhAoGADWv4kbu6ln/mO85t9/8KtNS883J+EWqg6RzyC+1l1UhM3+k7gjCPqaBtb4yGas2bkcSPbl8oq7inSWfCap5zmTkzcrrS3BxQuHH4XDRoqB/vrGHP5gD+KpfZxD+rFscfjVlk5FwhnYHrlRzE/kZr+MGOrtvc9TaluYzR0g18/dU= -----END RSA PRIVATE KEY-----
pri 私钥
1 -----BEGIN PUBLIC KEY----- 2 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAstVlLwOTcg64SiT4GiHOlToRxz88r3zjWX2N1juX86fa+UYtq33TfXZgh1rVl67dLJC7ICh4BYCrq1+JkMiyah0oOC6EVdLBWcKDrNV19ig5oetTuEO3Vbji/rU3nUYun24MmOJKca3xf4UL192pM5w+/mNHPLLHJBeoMjDWVJehURRw/PDGLv6+QFnC9LwihcR5vWr7x0lBAGJsPqRv9RUB0UWXj/AzxDeD6GGlHMS4tYwwYi6JQRHf9yA+dWDD4YTUc3UBb/WQx954ET/BW+KTFQzNwxryqr3XmucnZmkiNfTkmdGoH+J0zpVXSqHAvLkFJ9UERYUrUq0Bz6CpqQIDAQAB 3 -----END PUBLIC KEY-----
pub 公钥文件
settings文件
DEBUG = True ALLOWED_HOSTS = [‘*‘] MIDDLEWARE = [ ‘django.middleware.security.SecurityMiddleware‘, ‘django.contrib.sessions.middleware.SessionMiddleware‘, ‘django.middleware.common.CommonMiddleware‘, # ‘django.middleware.csrf.CsrfViewMiddleware‘, ‘django.contrib.auth.middleware.AuthenticationMiddleware‘, ‘django.contrib.messages.middleware.MessageMiddleware‘, ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘, ]#注意以上
settings
url
from django.conf.urls import url from django.contrib import admin from app import views urlpatterns = [ url(r‘^admin/‘, admin.site.urls), url(r‘^page1/‘, views.page1), url(r‘^page2/‘, views.page2), ]
url
html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link rel="stylesheet" href="dist/css/bootstrap.css"> 7 </head> 8 <body> 9 <form method="POST"> 10 {% csrf_token %} 11 <input type="text" name="money"> 12 <input type="submit" value="去支付" /> 13 </form> 14 15 16 <script></script> 17 </body> 18 </html>
html
utils
1 from datetime import datetime 2 from Crypto.PublicKey import RSA 3 from Crypto.Signature import PKCS1_v1_5 4 from Crypto.Hash import SHA256 5 from urllib.parse import quote_plus 6 from urllib.parse import urlparse, parse_qs 7 from base64 import decodebytes, encodebytes 8 import json 9 10 class AliPay(object): 11 """ 12 支付宝支付接口(PC端支付接口) 13 """ 14 15 def __init__(self, appid, app_notify_url, app_private_key_path, 16 alipay_public_key_path, return_url, debug=False): 17 self.appid = appid 18 self.app_notify_url = app_notify_url 19 self.app_private_key_path = app_private_key_path 20 self.app_private_key = None 21 self.return_url = return_url 22 with open(self.app_private_key_path) as fp: 23 self.app_private_key = RSA.importKey(fp.read()) 24 self.alipay_public_key_path = alipay_public_key_path 25 with open(self.alipay_public_key_path) as fp: 26 self.alipay_public_key = RSA.importKey(fp.read()) 27 28 if debug is True: 29 self.__gateway = "https://openapi.alipaydev.com/gateway.do" 30 else: 31 self.__gateway = "https://openapi.alipay.com/gateway.do" 32 33 def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs): 34 biz_content = { 35 "subject": subject, 36 "out_trade_no": out_trade_no, 37 "total_amount": total_amount, 38 "product_code": "FAST_INSTANT_TRADE_PAY", 39 # "qr_pay_mode":4 40 } 41 42 biz_content.update(kwargs) 43 data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url) 44 return self.sign_data(data) 45 46 def build_body(self, method, biz_content, return_url=None): 47 data = { 48 "app_id": self.appid, 49 "method": method, 50 "charset": "utf-8", 51 "sign_type": "RSA2", 52 "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 53 "version": "1.0", 54 "biz_content": biz_content 55 } 56 57 if return_url is not None: 58 data["notify_url"] = self.app_notify_url 59 data["return_url"] = self.return_url 60 61 return data 62 63 def sign_data(self, data): 64 data.pop("sign", None) 65 # 排序后的字符串 66 unsigned_items = self.ordered_data(data) 67 unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items) 68 sign = self.sign(unsigned_string.encode("utf-8")) 69 # ordered_items = self.ordered_data(data) 70 quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items) 71 72 # 获得最终的订单信息字符串 73 signed_string = quoted_string + "&sign=" + quote_plus(sign) 74 return signed_string 75 76 def ordered_data(self, data): 77 complex_keys = [] 78 for key, value in data.items(): 79 if isinstance(value, dict): 80 complex_keys.append(key) 81 82 # 将字典类型的数据dump出来 83 for key in complex_keys: 84 data[key] = json.dumps(data[key], separators=(‘,‘, ‘:‘)) 85 86 return sorted([(k, v) for k, v in data.items()]) 87 88 def sign(self, unsigned_string): 89 # 开始计算签名 90 key = self.app_private_key 91 signer = PKCS1_v1_5.new(key) 92 signature = signer.sign(SHA256.new(unsigned_string)) 93 # base64 编码,转换为unicode表示并移除回车 94 sign = encodebytes(signature).decode("utf8").replace("\n", "") 95 return sign 96 97 def _verify(self, raw_content, signature): 98 # 开始计算签名 99 key = self.alipay_public_key 100 signer = PKCS1_v1_5.new(key) 101 digest = SHA256.new() 102 digest.update(raw_content.encode("utf8")) 103 if signer.verify(digest, decodebytes(signature.encode("utf8"))): 104 return True 105 return False 106 107 def verify(self, data, signature): 108 if "sign_type" in data: 109 sign_type = data.pop("sign_type") 110 # 排序后的字符串 111 unsigned_items = self.ordered_data(data) 112 message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items) 113 return self._verify(message, signature)
utils
如果需要测试,需要用公网可以直接访问的url,
我们可以通过自己单机测试,更改本机hosts文件,将本机ip和views里return_url一致。
更改C:\Windows\System32\drivers\etc\hosts
最后从支付宝下载对应的沙河手机app,充值之后就可以付款了,快去尝试吧。
时间: 2024-11-05 22:02:35