1.登录蚂蚁金融开放平台:https://openhome.alipay.comjava
2.选择进入个人开放平台。寻找开发中心的研发服务。git
3.点击沙箱环境—沙箱应用github
4.这里博主已经配置好密钥了,因此在RSA2(SHA256)密钥(推荐)这边跟没有注册的不太同样。express
若是没有配置过密钥请继续向下看,密钥配置完毕跳到代码处json
5.下载RSA密钥生成工具:https://docs.open.alipay.com/291/105971api
根据网页中的使用步骤配置便可app
6.由于是沙箱测试,须要下载沙箱支付宝以便测试,可是沙箱支付宝仅支持安卓系统,须要一部安卓手机。函数
1.首先准备须要的库:工具
若是是使用pycharm,博主在本身的github上已经配好了所须要的库,测试
能够直接访问博主的github:https://github.com/PythonStriker/Alipay_for_QR_code
省下不少步骤若是不是使用paycharm,须要本身配置所须要的库:
2.代码以下所示:
pay.py
__author__ = 'PythonStriker' from self_Alipay import * import qrcode,time APPID = 本身的appid号 private_key = '''-----BEGIN RSA PRIVATE KEY----- 本身的支付宝私钥 -----END RSA PRIVATE KEY----- ''' public_key = '''-----BEGIN PUBLIC KEY----- 本身的支付宝公钥 -----END PUBLIC KEY----- ''' class pay: def __init__(self,out_trade_no,total_amount,subject,timeout_express): self.out_trade_no = out_trade_no self.total_amount = total_amount self.subject = subject self.timeout_express = timeout_express def get_qr_code(self,code_url): ''' 生成二维码 :return None ''' qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=10, border=1 ) qr.add_data(code_url) # 二维码所含信息 img = qr.make_image() # 生成二维码图片 img.save(r'本身须要保存的路径') print('二维码保存成功!') def query_order(self,out_trade_no: int): ''' :param out_trade_no: 商户订单号 :return: Nonem ''' _time = 0 for i in range(600): time.sleep(1) result = alipay.init_alipay_cfg().api_alipay_trade_query(out_trade_no=out_trade_no) if result.get("trade_status", "") == "TRADE_SUCCESS": print('订单已支付!') print('订单查询返回值:', result) return True _time += 2 return False if __name__ == '__main__': alipay = alipay(APPID, private_key, public_key) payer = pay(out_trade_no="订单号",total_amount= 价格,subject = "商品名字",timeout_express='订单超时取消时间 单位:s,m') dict = alipay.trade_pre_create(out_trade_no=payer.out_trade_no,total_amount=payer.total_amount,subject =payer.subject,timeout_express=payer.timeout_express ) payer.get_qr_code(dict['qr_code']) payer.query_order(payer.out_trade_no)
代码有几处须要注意的地方:公钥私钥,appid,二维码图片保存地址,主函数中payer实例化订单号,价格,商品名字,超市取消时间都是须要本身填写的!
在主函数中,调用的方法已经写出来了,能够在别的模块中用相同的调用方法,博主会在以后的文章中演示,如何在别的模块中,完成调用,并验证是否付款成功。
self_Alipay.py
# -*- coding: UTF-8 -*- import base64 import collections import copy import json from datetime import datetime from urllib import request, parse import rsa from alipay import AliPay APP_ID = '须要填写' private_key = '''-----BEGIN RSA PRIVATE KEY----- 须要填写 -----END RSA PRIVATE KEY----- ''' public_key = '''-----BEGIN PUBLIC KEY----- 须要填写 -----END PUBLIC KEY----- ''' class alipay: def __init__(self, app_id, private_key, public_key, notify_url=None, charset='gbk', sign_type='RSA2', version='1.0', DEBUG=True):#须要注意,本身编码类型是不是RSA2 self.requesturl = 'https://openapi.alipay.com/gateway.do' if DEBUG is False else "https://openapi.alipaydev.com/gateway.do" self.private_key = private_key self.public_key = public_key self.params = dict(app_id=app_id, charset=charset, sign_type=sign_type, version=version, biz_content={}, timestamp='', notify_url=notify_url) def _sort(self, params): #print(collections.OrderedDict(sorted(dict(params).items(), key=lambda x: x[0]))) return collections.OrderedDict(sorted(dict(params).items(), key=lambda x: x[0])) @staticmethod def make_goods_etail(goods_detail=None, alipay_goods_id=None, goods_name=None, quantity=None, price=None, goods_category=None, body=None, show_url=None): params = dict(goods_detail=goods_detail, alipay_goods_id=alipay_goods_id, goods_name=goods_name, quantity=quantity, price=price, goods_category=goods_category, body=body, show_url=show_url) return dict(filter(lambda x: x[1] is not None, params.items())) def _make_sign(self, params, **kwargs): private_key = rsa.PrivateKey.load_pkcs1(kwargs.get('private_key', None) or self.private_key) sign = base64.b64encode(rsa.sign(params.encode(), private_key, "SHA-256")).decode('gbk') return sign def _check_sign(self, message, sign, **kwargs): message = self._sort(message) data = '{' for key, value in message.items(): data += '"{}":"{}",'.format(key, value) data = data[:-1] + '}' sign = base64.b64decode(sign) public_key = rsa.PublicKey.load_pkcs1_openssl_pem(kwargs.get('public_key', None) or self.public_key) try: rsa.verify(data.encode(), sign, public_key) return True except Exception: return False def _make_request(self, params, biz_content, **kwargs): buf = '' params['timestamp'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') params['biz_content'] = json.dumps(self._sort(biz_content)) for key, value in kwargs.items(): params[key] = value params = self._sort(params) for key in params: buf += '{}={}&'.format(key, params[key]) params['sign'] = self._make_sign(buf[:-1], **kwargs) #print(params) # 发射http请求取回数据 data = request.urlopen(self.requesturl, data=parse.urlencode(params).encode('gbk')).read().decode('gbk') #print(parse.urlencode(params).encode('gbk')) return data def parse_response(self, params, **kwargs): sign = params['sign'] if self._check_sign(dict(filter(lambda x: 'sign' not in x[0], params.items())), sign, **kwargs): return True else: return False def trade_pre_create(self, out_trade_no, total_amount, subject, seller_id=None, discountable_amount=None, undiscountable_amount=None, buyer_logon_id=None, body=None, goods_detail=None, operator_id=None, store_id=None, terminal_id=None, timeout_express=None, alipay_store_id=None, royalty_info=None, extend_params=None, **kwargs): """ :param out_trade_no: 商户订单号,64个字符之内、只能包含字母、数字、下划线;需保证在商户端不重复. :param total_amount: 订单总金额,单位为元,精确到小数点后两位. :param subject: 订单标题. :param seller_id: 卖家支付宝用户ID。 若是该值为空,则默认为商户签约帐号对应的支付宝用户ID. :param discountable_amount:可打折金额. 参与优惠计算的金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] :param undiscountable_amount:不可打折金额. 不参与优惠计算的金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] :param buyer_logon_id: 买家支付宝帐号 :param body: 对交易或商品的描述 :param goods_detail: 订单包含的商品列表信息.使用make_goods_etail生成. 其它说明详见:“商品明细说明” :param operator_id: 商户操做员编号 :param store_id: 商户门店编号 :param terminal_id: 商户机具终端编号 :param timeout_express: 该笔订单容许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天 :param alipay_store_id: 支付宝店铺的门店ID :param royalty_info: 描述分帐信息 暂时无效 :param extend_params: 业务扩展参数 暂时无效 :param kwargs: 公共参数可在此处暂时覆盖 :return: """ params = copy.deepcopy(self.params) params['method'] = 'alipay.trade.precreate' total_amount = round(int(total_amount), 2) if discountable_amount: discountable_amount = round(int(discountable_amount), 2) if undiscountable_amount: undiscountable_amount = round(int(undiscountable_amount), 2) if discountable_amount: if undiscountable_amount is not None: if discountable_amount + undiscountable_amount != total_amount: return '传入打折金额错误' biz_content = dict(out_trade_no=out_trade_no[:64], total_amount=total_amount, seller_id=seller_id, subject=subject, discountable_amount=discountable_amount, undiscountable_amount=undiscountable_amount, buyer_logon_id=buyer_logon_id, body=body, goods_detail=goods_detail, operator_id=operator_id, store_id=store_id, terminal_id=terminal_id, timeout_express=timeout_express, alipay_store_id=alipay_store_id, royalty_info=royalty_info, extend_params=extend_params) #print(biz_content) resp = self._make_request(params, dict(filter(lambda x: x[1] is not None, biz_content.items())), **kwargs) #print(resp) check = eval(resp) resp = json.loads(resp)['alipay_trade_precreate_response'] if self._check_sign(check['alipay_trade_precreate_response'], check['sign']): return resp return False def trade_refund(self, refund_amount, out_trade_no=None, trade_no=None, refund_reason=None, out_request_no=None, operator_id=None, store_id=None, terminal_id=None, **kwargs): """ :param refund_amount: 须要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数 :param out_trade_no: 商户订单号,不可与支付宝交易号同时为空 :param trade_no: 支付宝交易号,和商户订单号不能同时为空 :param refund_reason: 退款的缘由说明 :param out_request_no: 标识一次退款请求,同一笔交易屡次退款须要保证惟一,如需部分退款,则此参数必传。 :param operator_id: 商户的操做员编号 :param store_id: 商户的门店编号 :param terminal_id: 商户的终端编号 :param kwargs: 公共参数可在此处临时覆盖 :return: """ params = copy.deepcopy(self.params) params['method'] = 'alipay.trade.refund' refund_amount = round(float(refund_amount), 2) biz_content = dict(refund_amount=refund_amount, out_trade_no=out_trade_no, trade_no=trade_no, refund_reason=refund_reason, out_request_no=out_request_no, operator_id=operator_id, store_id=store_id, terminal_id=terminal_id) resp = self._make_request(params, dict(filter(lambda x: x[1] is not None, biz_content.items())), **kwargs) check = eval(resp) resp = json.loads(resp)['alipay_trade_refund_response'] if self._check_sign(check['alipay_trade_refund_response'], check['sign']): return int(resp['code']) == 10000 return False def trade_query(self, out_trade_no, trade_no=None, **kwargs): params = copy.deepcopy(self.params) params['method'] = 'alipay.trade.query' biz_content = dict(out_trade_no=out_trade_no, trade_no=trade_no) resp = self._make_request(params, dict(filter(lambda x: x[1] is not None, biz_content.items())), **kwargs) check = eval(resp) resp = json.loads(resp)['alipay_trade_query_response'] if self._check_sign(check['alipay_trade_query_response'], check['sign']) and resp['code'] == 10000: return resp return False def init_alipay_cfg(self): alipay = AliPay( appid=APP_ID, app_notify_url=None, # 默认回调url app_private_key_string=private_key, alipay_public_key_string=public_key, # 支付宝的公钥,验证支付宝回传消息使用,不是你本身的公钥, sign_type="RSA2", # RSA 或者 RSA2 debug=True # 默认False ,若开启则使用沙盒环境的支付宝公钥 ) return alipay
该代码也有几处须要注意的地方:公钥密钥,appid,特别注意本身加密形式RSA 或者 RSA2以前博主吃过大亏。
#---------------------------------------------------------------------------------------------------------------------------#
特别注意事项:
1.这个支付宝端口支持的是非java端口,使用PKCS1加密方式密匙。
2.支付宝应用公钥,和支付宝公钥要分清楚。本代码中须要填写的是支付宝公钥!
3.其实PKCS8的小伙伴也不用悲伤,支付宝自带格式转换,以下图:
只须要将本身的PKCS8(JAVA适用)转换PKCS1(非JAVA适用)的密钥,本文代码依旧可使用。
#---------------------------------------------------------------------------------------------------------------------------#
能够用本身的沙箱支付宝测试,是否能够支付。
附加成功示例: