1、设置沙箱帐户css
一、经过支付宝登录到蚂蚁金服开放平台(https://openhome.alipay.com/platform/appDaily.htm?tab=info)html
注释:只要登录上后沙箱应用上面就有本身的APPID和支付宝网关,支付宝网关就是支付宝给咱们的api接口,而RSA2(SHA256)密钥是须要本身设置德。python
二、设置密钥django
下载支付宝自动生成密钥软件,将本身的应用私钥保存在本地,应用公钥发送给支付宝点击确认后就会获得支付宝发送给咱们的支付宝公钥json
三、下载支付宝钱包bootstrap
经过手机扫码下载支付宝沙箱钱包,沙箱钱包的用户名和密码在在沙箱帐户中查找api
2、设置Alipay服务器
一、因为支付宝不提供python版本的sdk,因此须要手动编写python版本sdk。app
二、编写python版本sdk框架
一、项目目录结构
二、settings配置(主要写一些必须的配置项)
import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class AliPayConfig(object): # 商户app_id,即沙箱的APPID app_id = "2016081500252288" # 商户私钥路径 merchant_private_key_path = os.path.join(BASE_DIR, "keys", "app_private_2048.txt") # 支付宝公钥路径 alipay_public_key_path = os.path.join(BASE_DIR, "keys", "alipay_public_2048.txt") # 服务器异步通知页面路径 需http: // 格式的完整路径,不能加?id = 123 这类自定义参数,必须外网能够正常访问 # 向支付宝支付成功后,支付宝会执行回调函数来通知服务器的支付状态,即支付宝发送post请求是支付宝支付成功后调用服务器的验证函数,服务器来判断是不是真的支付成功 notify_url = "http://47.94.172.250:8804/api/v1/trade/alipay/" # 页面跳转同步通知页面路径 需http: // 格式的完整路径,不能加?id = 123 这类自定义参数,必须外网能够正常访问 # 向支付宝支付成功后,支付宝会执行回调函数来通知服务器的支付状态,即支付宝发送get请求是支付宝支付成功后调用服务器的支付成功的页面信息 return_url = "http://47.94.172.250:8804/api/v1/trade/alipay/" # 签名方式(当前只支持RSA和RSA2) sign_type = "RSA2" # 字符编码格式 charset = "utf-8" # 支付宝网关(若是是线上环境的话, dev 这三个字去掉便可) gateway_url = "https://openapi.alipaydev.com/gateway.do" # 异步通知参数DOC(支付宝会主动发起POST请求) notify_doc = "https://docs.open.alipay.com/270/105902/"
三、支付宝公钥和应用私钥配置
注释:一、主要是存放在keys文件中,alipay_public_2048.txt中保存的是支付宝公钥,app_private_2048.txt中保存的是应用私钥。
二、不管是支付宝公钥仍是应用私钥在文件中的写法必须遵循以下格式:
-----BEGIN RSA PRIVATE KEY-----
中间写支付宝公钥或是应用私钥
-----END RSA PRIVATE KEY-----
四、alipay文件配置
注释:配置以前必须先安装Crypto和pycryptodome俩模块
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 from config.settings import AliPayConfig import json # 文档连接(支付成功回调通知参数等相关, 支付宝交易状态是 TRADE_SUCCESS 会主动发起异步通知) # https://docs.open.alipay.com/270/105902/ # 沙箱测试环境可以使用沙箱支付宝支付 # 帐号: rjoegs7144@sandbox.com # 密码: 111111 # 支付密码: 111111 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()) if debug is True: #alipaydev是测试环境,alipay是生产环境 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) #上面的类就是支付宝的api接口,如下只须要调实例化产生api接口类对象后 if __name__ == "__main__": #支付宝的api接口类实例化 alipay = AliPay( appid=AliPayConfig.app_id, #APPDI app_notify_url=AliPayConfig.notify_url, #支付成功后的post请求 return_url=AliPayConfig.return_url, #支付成功后的get请求 app_private_key_path=AliPayConfig.merchant_private_key_path, #应用私钥 alipay_public_key_path=AliPayConfig.alipay_public_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你本身的公钥 debug=True, # 默认False,是不是测试环境仍是生产环境 ) #调用实例化对象下的direct_pay方法,生成支付的url import time query_params = alipay.direct_pay( subject="luffycity", # 商品简单描述,可自定义哦 out_trade_no="%s"time.time(), # 商户订单号,可自定义可是商品订单号必须是惟一的,不然发送给支付宝不会被处理 total_amount=100.00, # 交易金额(单位: 元 保留俩位小数) ) pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)#这就是生成的支付宝api接口 print(pay_url) # 这一部分是验证url的签名,即支付成功后支付宝经过post请求返回给服务器的验证数据 return_url = 'http://47.92.87.172:8000/?total_amount=100.00×tamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2Fvhd DYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdp VQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6 Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&meth od=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0' #这一部分就是支付宝返回给服务器的数据 o = urlparse(return_url) #路径解析,就至关因而取request.body中的数据 query = parse_qs(o.query) #将数据转换成字典 processed_query = {} ali_sign = query.pop("sign")[0] for key, value in query.items(): processed_query[key] = value[0] # 验证签名校验的结果,就是支付宝发的post请求验证 print(alipay.verify(processed_query, ali_sign)) #调用alipay对象下的verify认证方法,若是返回True就证实支付成功,不然失败
三、根据步骤2中的讲解,本身编写支付宝支付框架
一、如图所示:
二、pay.py文件(该文件内就只写了个类,该类就是支付宝api的接口类)
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()) 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)
三、page1.html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="dist/css/bootstrap.css"> </head> <body> <form method="POST"> {% csrf_token %} <input type="text" name="money"> <input type="submit" value="去支付" /> </form> <script></script> </body> </html>
四、url.py文件
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^page1/', views.page1), url(r'^page2/', views.page2), ]
五、key文件夹
一、pri文件存放的是应用私钥
二、pub文件存放的是支付宝公钥
六、views.py文件
from django.shortcuts import render, redirect, HttpResponse from utils.pay import AliPay import json import time #将实例化的api接口和参数存放子啊函数中 def ali(): #APPID app_id = "2016082500309412" # POST请求,支付宝回调函数,用于支付成功后的验证 notify_url = "http://www.dabo.com:8009/page2/" # GET请求,支付宝回调函数,用于支付成功后返回给用户的页面信息 return_url = "http://www.dabo.com:8009/page2/" merchant_private_key_path = "keys/pri" #应用私钥 alipay_public_key_path = "keys/pub" #支付宝公钥 #实例化api接口类 alipay = AliPay( appid=app_id, app_notify_url=notify_url, return_url=return_url, app_private_key_path=merchant_private_key_path, alipay_public_key_path=alipay_public_key_path, debug=True, # 默认False,若是是True则为测试环境,若是为Flase则为生成环境 ) return alipay def page1(request): if request.method == "GET": return render(request, 'page1.html') else: money = float(request.POST.get('money')) alipay = ali() # 生成支付的url query_params = alipay.direct_pay( #调用支付宝对象下的direct_pay方法 subject="充气式韩红", # 商品简单描述 out_trade_no="x2" + str(time.time()), # 商户订单号,有切必须惟一 total_amount=money, # 交易金额(单位: 元 保留俩位小数) ) #支付宝api完整路径 pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params) return redirect(pay_url) def page2(request): alipay = ali() if request.method == "POST": # 检测是否支付成功 # 去请求体中获取全部返回的数据:状态/订单号 from urllib.parse import parse_qs """ # request.body => 字节类型 # request.body.decode('utf-8') => 字符串类型 {"k1":["v1"],"k2":["v1"]} k1=[v1]&k2=[v2] """ body_str = request.body.decode('utf-8') post_data = parse_qs(body_str)#parse_qs是将字符串直接json成字典的形式 # {k1:[v1,],k2:[v2,]},这是支付宝返回给咱们的json数据,可是不是咱们想要的数据, post_dict = {} for k, v in post_data.items():#因此经过这种方式能够转换成咱们想要的{k1:v1}格式的数据 post_dict[k] = v[0] print(post_dict) """ {'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'} {'stade_status': "trade_success",'order':'x2123123123123'} """ sign = post_dict.pop('sign', None) status = alipay.verify(post_dict, sign)#把数据和签名放在一块儿进行进行校验,验证是否支付成功 if status: #print(post_dict['stade_status'])#支付宝系统的交易状态,stade_status这个写的有点问题,单词好像写错了 print(post_dict['out_trade_no'])#商品网站惟一订单号 return HttpResponse('POST返回') else: # QueryDict = {'k':[1],'k1':[11,22,3]} params = request.GET.dict() sign = params.pop('sign', None) status = alipay.verify(params, sign) print('GET验证', status) return HttpResponse('支付成功')