python Alipay payment
WeChat and Alipay SDK Download
1. Sandbox environment configuration
Offered by Alipay sandbox environment In the development, use your Alipay account to apply for a sandbox account.
Can refer to Sandbox instructions Get AppID and so on, then download the Android version of Alipay wallet according to the instructions, and log in with the buyer account of sandbox environment.
Fill in the authorization callback address and the selected signing method (RSA2) on the platform.
Note: if it is officially launched, you can't use the AppID of the sandbox, you should go to Alipay open platform Create a life number or applet to get it.
2. Configure key and public key.
It can be generated using the official key tool To help us generate two kinds of keys. The key length is RSA2(2048) and the key format is PKCS1. Upload the generated key to Alipay backstage.
Refer to the official instructions
3. Installing third-party libraries in a virtual environment
pip install alipay-sdk-python==3.3.398
Note: to install under windows, you need to execute a command in the virtual environment first
set CL=/FI"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include\\stdint.h"
Then execute
pip install alipay-sdk-python==3.3.398
If you use the Crypto library for symmetric encryption, you need to import another library
pip install pycryptodome
You can refer to the blog installation Click me to jump
4. Payment process description
For details, please refer to Alipay official document description There are different payment scenarios such as face-to-face payment, App payment and mobile website payment.
5. Description of code scanning process
1. User scanning QR code
2, the back end determines whether the source of scanning code is Alipay.
3, go to Alipay to request auth_code
4. Through auth_code to get the user of the user_id
5. After the code scanning is successful, the user's mobile terminal displays the payment amount and the confirm payment button
6. Click to confirm payment
7, get Alipay payment url back to the front end, front end tune up Alipay cashier.
8. The user enters the payment password to complete the payment
9. Front end synchronization notification after successful payment
10. Alipay asynchronous notification, such as backend, to modify order status.
6. Core code
[note] the following code is based on Django framework
6.1 verify whether the user uses WeChat or Alipay scan code.
def user_agent_auth(func): # Verify whether the user uses WeChat or Alipay scan code. @wraps(func) def wrapper(request, *args, **kwargs): user_agent = request.META.get('HTTP_USER_AGENT') if 'MicroMessenger' not in user_agent and 'AlipayClient' not in user_agent: return render(request, 'error.html', context={'message': 'Please use WeChat or Alipay scan code.'}) return func(request, *args, **kwargs) return wrapper @user_agent_auth def scan_qrcode(request, *args, **kwargs): # Judge whether the user uses WeChat or Alipay scan code. user_agent = request.META.get('HTTP_USER_AGENT') params = request.GET.get('params') # After scanning the code, the user parses the main parameters from the url # Set the callback url to http: / / your domain name / order/pay_page/ redirect_uri = '{}{}?params={}'.format(request.build_absolute_uri('/'), 'order/pay_page/', params) if 'AlipayClient' in user_agent: # Use Alipay scan code # Request Alipay to get auth_code, and then get user_id # Reference to official documents https://opendocs.alipay.com/open/289/105656 # Get payment parameters set in the background alipay_setting = PayConfig.objects.filter(is_use__in=['pron', 'test'], ali_appid__isnull=False) if alipay_setting: return HttpResponseRedirect(AliUserInfo().get_ali_auth_code(alipay_setting.first(), redirect_uri)) message = 'Alipay payment parameters are not yet configured.' else: message = 'Please use Alipay sweep code payment.' return render(request, 'error.html', context={'message': message})
The core code of Alipay payment is placed on alipay_. util. Py file, alipay_util.py code is as follows:
# -*- coding: utf-8 -*- import json import threading import urllib from apps.orderinfo.models import create_transaction, MallGoods, PaymentTransaction, payed_action from apps.paysettings.models import PayConfig try: """ Note: windows Possible installation under Crypto,alipay-sdk-python Will fail. The solutions are as follows First execute a command in the virtual environment set CL=/FI"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include\\stdint.h" Then execute pip install alipay-sdk-python==3.3.398 pip install pycryptodome """ from alipay.aop.api.AlipayClientConfig import AlipayClientConfig # Used to create the configuration class required by the client object from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient # Used to create client objects # Class required for Mobile Website Payment from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayModel, AlipayTradeWapPayRequest from alipay.aop.api.domain.AlipayTradeQueryModel import AlipayTradeQueryModel from alipay.aop.api.domain.AlipayTradeRefundModel import AlipayTradeRefundModel from alipay.aop.api.request.AlipayTradeQueryRequest import AlipayTradeQueryRequest from alipay.aop.api.request.AlipayTradeRefundRequest import AlipayTradeRefundRequest from alipay.aop.api.util.SignatureUtils import get_sign_content, verify_with_rsa except: AlipayClientConfig = None DefaultAlipayClient = None AlipayTradeWapPayModel = None AlipayTradeWapPayRequest = None AlipayTradeQueryModel = None AlipayTradeRefundModel = None AlipayTradeQueryRequest = None AlipayTradeRefundRequest = None get_sign_content = None verify_with_rsa = None class AliPayUtil(): # Alipay payment interface official document https://opendocs.alipay.com/apis/api_1/alipay.trade.query def get_client(self, params): # Instantiate client alipay_client_config = AlipayClientConfig(params.get('debug')) # Create configuration class, True: sandbox environment, False: formal environment alipay_client_config.app_id = params.get('appid') # Specify appid # Specify your own private key for Alipay check. alipay_client_config.app_private_key = params.get('app_private_key') # Specify the public key of Alipay for Alipay inspection. alipay_client_config.alipay_public_key = params.get('alipay_public_key') return DefaultAlipayClient(alipay_client_config) # Instantiate client object def get_pay_url(self, params): # Get payment url client = self.get_client(params) # Instantiate client object # Parameters required to construct jump links (I only fill in a few required parameters here. For details, please refer to the description of request parameters in the development document) model = AlipayTradeWapPayModel() model.out_trade_no = params.get('out_trade_no') # Order number, generated in the background (cannot be repeated) model.total_amount = params.get('total_amount') # Price to be paid model.subject = params.get('subject') # Order header information description model.product_code = "QUICK_WAP_WAY" # Fixed parameters of Mobile Website Payment model.passback_params = params.get('passback_params') # Additional parameters model.quit_url = params.get('return_url') # A required parameter, string, is the address of the merchant's website when the user exits halfway through payment request = AlipayTradeWapPayRequest(biz_model=model) # Create request object request.return_url = params.get('return_url') # Bounce back address after successful payment request.notify_url = params.get('notify_url') # Notification address after successful payment # Execute API call alipay_url = client.page_execute(request, "GET") # Generate the jump link url, or specify the jump in POST mode, but the front-end processing is slightly more complex than GET mode return alipay_url # Return to jump link to front page def check_pay_sign(self, key, params, sign): # Define functions to check payment results if "sign_type" in params: sign_type = params.pop("sign_type") sign_content = get_sign_content(params) try: return verify_with_rsa(key, sign_content.encode('utf-8'), sign) # Verify signature and get results except: # If the verification fails, a false value is returned. return False def get_query_response(self, params): # Get transaction query results client = self.get_client(params) # Instantiate client object model = AlipayTradeQueryModel() model.out_trade_no = params.get('out_trade_no') model.query_options = ['trade_settle_info'] request = AlipayTradeQueryRequest(biz_model=model) return json.loads(client.execute(request)) def refund(self, params): # refund client = self.get_client(params) # Instantiate client object model = AlipayTradeRefundModel() model.trade_no = params.get('tran_trade_no') model.out_request_no = 'HZ01RF001' model.refund_amount = params.get('total_amount') model.refund_reason = u'Normal refund' request = AlipayTradeRefundRequest(biz_model=model) response = json.loads(client.execute(request)) return response class AliUserInfo(): # Get Alipay user information def get_ali_auth_code(self, alipay_setting, redirect_uri): """ Getting redirection from Alipay url :return: """ if alipay_setting.is_use == 'test': domain = 'alipaydev.com' else: domain = 'alipay.com' url = 'https://openauth.{}/oauth2/publicAppAuthorize.htm?app_id={}&scope={}&redirect_uri={}'.format( domain, alipay_setting.ali_appid, 'auth_base', urllib.parse.quote(redirect_uri) ) return url def get_ali_user_id(self, alipay_setting, auth_code): # Get the unique identifier of Alipay life number from alipay.aop.api.AlipayClientConfig import AlipayClientConfig # Used to create the configuration class required by the client object from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient # Used to create client objects from alipay.aop.api.request.AlipaySystemOauthTokenRequest import AlipaySystemOauthTokenRequest from alipay.aop.api.response.AlipaySystemOauthTokenResponse import AlipaySystemOauthTokenResponse alipay_client_config = AlipayClientConfig( False if alipay_setting.is_use == 'pron' else True) # Create configuration class, True: sandbox environment, False: formal environment alipay_client_config.app_id = alipay_setting.ali_appid # Specify appid alipay_client_config.app_private_key = alipay_setting.app_private_key alipay_client_config.alipay_public_key = alipay_setting.alipay_public_key client = DefaultAlipayClient(alipay_client_config) # Instantiate client object request = AlipaySystemOauthTokenRequest() request.code = auth_code request.grant_type = "authorization_code" # Execute API call, that is, send a request to Alipay. try: response_content = client.execute(request) except Exception as e: return {'error': 1, 'message': str(e)} if not response_content: return {'error': 1, 'message': 'Failed to get Alipay user information request'} else: response = AlipaySystemOauthTokenResponse() # Analytical response results response.parse_response_content(response_content) if response.is_success(): # If the business is successful, you can obtain the required value through the response attribute auth_token = response.access_token user_id = response.user_id return {'error': 0, 'auth_token': auth_token, 'user_id': user_id} # Business processing of response failure else: # If the business fails, you can know the error from the error code. For specific error code information, you can view the interface document message = response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg return {'error': 1, 'message': message} class ALIPAYPaymentTransaction(): # Alipay unified payment url, query Alipay transaction records, refund def alipay_page_params(self, auth_code, goods_ids, total_amount, params, payconfig, pay_params_url): # Get Alipay page payment parameters # Get user information of life number user_info = AliUserInfo().get_ali_user_id(payconfig, auth_code) if user_info['error']: return {'error': 1, 'message': user_info['message']} user_id = user_info['user_id'] kw = { 'amount': total_amount, 'scan_source': 'Alipay', 'user_unionid': user_id, 'payconfig_id': payconfig.id, 'goods_ids': goods_ids, } transaction = create_transaction(kw) if transaction['error']: return transaction goods_queryset = MallGoods.objects.filter(id__in=goods_ids) ali_pay_params = { 'error': 0, 'total_amount': total_amount, # amount of money 'partner_name': 'user', # Customer name 'goods_queryset': goods_queryset, # Product list 'company_name': 'Online aggregate payment', # corporate name 'pay_params_url': pay_params_url, # Get payment parameter url 'user_id': user_id, # User user_id 'params': params, # Encryption parameters carried by code scanning 'transaction_id': transaction['transaction_id'], # Transaction id } return ali_pay_params def get_alipay_url(self, params): """ Get Alipay payment url :param total_amount: Payment amount :param out_trade_no: order number :param attach: Additional parameters, payment.acquirer Medium id :param notify_url: Back end asynchronous callback url :param return_url: Front end synchronization callback url :param payment_acquirer: Online payment configuration parameter object :return: payment url """ alipay_setting = params.get('payconfig') pay_params = { 'debug': False if alipay_setting.is_use == 'pron' else True, # pron stands for using a formal environment 'appid': alipay_setting.ali_appid, 'app_private_key': alipay_setting.app_private_key, 'alipay_public_key': alipay_setting.alipay_public_key, 'out_trade_no': params.get('out_trade_no'), 'total_amount': params.get('total_amount'), 'subject': "Aggregate payment", 'passback_params': params.get('attach'), 'return_url': params.get('return_url'), 'notify_url': params.get('notify_url'), } url = AliPayUtil().get_pay_url(pay_params) return url def alipay_update_order(self, kw): """ If payment is successful, Alipay will send it to this address. POST Request (check whether the payment has been completed) :param args: :param kw: {'gmt_create': '2020-10-29 16:13:02', 'charset': 'utf-8', 'seller_email': 'nyyxxx@sandbox.com', 'subject': 'Online aggregate payment', 'sign': 'f5CzBrY13s', 'buyer_id': '20881xxxxx52', 'invoice_amount': '9.00', 'notify_id': '202010290 xxxx94250508020641', 'fund_bill_list': '[{"amount":"9.00","fundChannel":"ALIPAYACCOUNT"}]', 'notify_type': 'trade_status_sync', 'trade_status': 'TRADE_SUCCESS', 'receipt_amount': '9.00', 'buyer_pay_amount': '9.00', 'app_id': '201609xxxxxxx2540', 'sign_type': 'RSA2', 'seller_id': '20881xxxxxx411', 'gmt_payment': '2020-10-29 16:13:02', 'notify_time': '2020-10-29 16:13:03', 'passback_params': '{"transaction_id": "176", "scan_type": "invoice"}', 'version': '1.0', 'out_trade_no': 'xxx', 'total_amount': '9.00', 'trade_no': 'xxx', 'auth_app_id': 'xxx', 'buyer_logon_id': 'irb***@sandbox.com', 'point_amount': '0.00'} :return: """ # Modify Alipay payment order payment.transaction if kw.get('trade_status') == 'TRADE_SUCCESS': # Successful trade alipay_sign = kw.pop('sign') payconfig = PayConfig.objects.filter(is_use__in=['pron', 'test']).first() if AliPayUtil().check_pay_sign(payconfig.alipay_public_key, kw, alipay_sign): # Verify parameter signature successfully # Modify transaction as paid passback_params = json.loads(kw.get('passback_params')) # Additional parameters if passback_params and passback_params.get('transaction_id'): transaction_id = passback_params['transaction_id'] PaymentTransaction.objects.filter(id=transaction_id).update(tran_trade_no=kw.get('trade_no'), state='payed') th = threading.Thread(target=payed_action, args=(transaction_id,)) th.daemon= False # Non daemon thread, which does not hang up with the main thread th.start() return 'success' return 'fail' def query_state(self, params): # View order status O: unpaid P: paid C: cancelled R: refunded I: paying N: order does not exist F: payment failed T: order timeout # return: String [O, P, C, R, I, N, F, T] alipay_setting = params.get('payconfig') query_params = { 'debug': False if alipay_setting.is_use == 'pron' else True, # pron stands for using a formal environment 'appid': alipay_setting.ali_appid, 'app_private_key': alipay_setting.app_private_key, 'alipay_public_key': alipay_setting.alipay_public_key, 'out_trade_no': params.get('out_trade_no'), } response = AliPayUtil().get_query_response(query_params) if response.get('code') == '10000': if response.get('trade_status') == 'TRADE_SUCCESS': return 'P', response.get('trade_no') elif response.get('trade_status') == 'WAIT_BUYER_PAY': return 'I', None elif response.get('trade_status') == 'TRADE_CLOSED': return 'R', None return 'N', None def refund(self, params): # refund alipay_setting = params.get('payconfig') total_fee = params.get('amount') refund_fee = params.get('refund_fee') if params.get('refund_fee') else total_fee refund_params = { 'debug': False if alipay_setting.is_use == 'pron' else True, # pron stands for using a formal environment 'appid': alipay_setting.ali_appid, 'app_private_key': alipay_setting.app_private_key, 'alipay_public_key': alipay_setting.alipay_public_key, 'tran_trade_no': params.get('tran_trade_no'), 'total_amount': refund_fee, } response = AliPayUtil().refund(refund_params) if response.get('code') == '10000': if response.get('fund_change') == 'Y': return {'error': 0, 'message': response.get('msg'), 'tran_trade_no': response['trade_no'], 'refund_order_no': response['trade_no'], 'refund_reason': 'Normal refund', 'refund_person': response['buyer_user_id'], 'refund_time': response['gmt_refund_pay'], 'refund_amount': response['refund_fee'], 'refund_way': 'alipay', } return {'error': 1, 'message': 'Refund failed. The money has been refunded'} return {'error': 1, 'message': response.get('sub_msg')}
7. Interface display
8. Order query
After you click confirm payment, you can view the order changes in the background:
You can operate orders through status query and refund.
WeChat and Alipay SDK Download
Postscript
[postscript] in order to enable everyone to learn programming easily, I created a public official account, which includes programming for quick learning, and some dry cargo to improve your programming level. There are also some programming projects suitable for some courses design.
You can also add wechat [1257309054] to pull you into the group and exchange and learn together.
If the article is helpful to you, please buy me a cup of coffee!
official account
Pay attention to me and we will grow together~~