python Alipay two-dimensional code payment source code

Posted by porto88 on Sun, 20 Feb 2022 03:17:38 +0100

python Alipay payment

WeChat and Alipay SDK Download

Click me to 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

GitHub reference

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

Click me to 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~~

Topics: Python