Python Alipay SDK with SHA1/SHA256 support
Clone or download

README.md

python-alipay-sdk

PyPI version codecov travis-ci

中文文档

AliPay Python SDK

I can't find any official python alipay sdks so far, and it really pains a lot dealing with those sign methods. Hoping this library could do some help :).

So far, the following functions are supported:

Taking a look at this guide if you are interested at the details on signing your order requests. Or you may just follow this manual if not.

GUIDE

Installation

# installation
pip install python-alipay-sdk --upgrade

Cert generation

# openssl
OpenSSL> genrsa -out app_private_key.pem 2048  # the private key file
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # export public key
OpenSSL> exit

The public key we download from open.alipay.com is a string, which cannot be recognied by this lib directly, making sure it's surrounded with -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----

There is also an example for your reference

Intialization

from alipay import AliPay, ISVAliPay

# Making sure your key file is adhered to standards.
# you may find examples at tests/certs/ali/ali_private_key.pem
app_private_key_string = open("/path/to/your/private/key.pem").read()
alipay_public_key_string = open("/path/to/alipay/public/key.pem").read()

app_private_key_string = """
    -----BEGIN RSA PRIVATE KEY-----
    base64 encoded content
    -----END RSA PRIVATE KEY-----
"""

alipay_public_key_string = """
    -----BEGIN PUBLIC KEY-----
    base64 encoded content
    -----END PUBLIC KEY-----
"""

alipay = AliPay(
    appid="",
    app_notify_url=None,  # the default notify path
    app_private_key_string=app_private_key_string,
    # alipay public key, do not use your public key!
    alipay_public_key_string=alipay_public_key_string,
    sign_type="RSA" # RSA or RSA2
    debug=False  # False by default
)


# If you don't know what ISV is, then forget about what I mentioned below
# either app_auth_code or app_auth_token should not be None
isv_alipay = ISVAliPay(
    appid="",
    app_notify_url=None,  # the default notify path
    app_private_key_string="",
    # alipay public key, do not use your public key!
    alipay_public_key_string=alipay_public_key_string,
    sign_type="RSA" # RSA or RSA2
    debug=False  # False by default,
    app_auth_code=None,
    app_auth_token=None
)

Naming convention

Given an alipay function, say alipay.trade.page.pay, we will defind a corresponding function alipay.api_alipay_trade_page_pay()

Generally we will do such a translation:

function_name = "api_" + alipay_function_name.replace(".", "_")

According to alipay document, some paremeters in biz_content are optional and some are not. We defind functions in this way so that you can put those optional parameters in kwargs:

def api_alipay_xxx(self, out_trade, total_amount, **kwargs):
    ...
    biz_content.update(kwargs)

alipay.trade.page.pay

# For Python 2 users(you should really think about Python 3), making sure non-ascii strings are utf-8 encoded
subject = u"测试订单".encode("utf8")
# For Python 3 users, just use the default string
subject = "测试订单"

# Pay via Web,open this url in your browser: https://openapi.alipay.com/gateway.do? + order_string
order_string = alipay.api_alipay_trade_page_pay    (
    out_trade_no="20161112",
    total_amount=0.01,
    subject=subject,
    return_url="https://example.com",
    notify_url="https://example.com/notify" # this is optional
)

alipay.trade.wap.pay

# Pay via WAP, open this url in your browser: https://openapi.alipay.com/gateway.do? + order_string
order_string = alipay.api_alipay_trade_wap_pay(
    out_trade_no="20161112",
    total_amount=0.01,
    subject=subject,
    return_url="http://example.com",
    notify_url="https://example.com/notify" # this is optional
)

alipay.trade.app.pay

# Pay via App,just pass order_string to your Android or iOS client
order_string = alipay.api_alipay_trade_app_pay(
    out_trade_no="20161112",
    total_amount=0.01,
    subject=subject,
    notify_url="https://example.com/notify" # this is optional
)

Notification Validation

Once an order is paid, you will get a POST request from alipay servers which informs you that the order is paid

Here is a simple example for flask web server:

import json
from flask import Flask
from flask import request
app = Flask(__name__)

@app.route('/', methods=["GET", "POST"])
def hello_world():
    data = request.form.to_dict()
    # sign must be poped out
    signature = data.pop("sign")

    print(json.dumps(data))
    print(signature)

    # verify
    success = alipay.verify(data, signature)
    if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED" ):
        print("trade succeed")
    return 'Hello, World!'

Here is a more general example for verification

# gathering all parameters sent from alipay server, and put them in a dictionary called data
data = {
     "subject": "testing order",
     "gmt_payment": "2016-11-16 11:42:19",
     "charset": "utf-8",
     "seller_id": "xxxx",
     "trade_status": "TRADE_SUCCESS",
     "buyer_id": "xxxx",
     "auth_app_id": "xxxx",
     "buyer_pay_amount": "0.01",
     "version": "1.0",
     "gmt_create": "2016-11-16 11:42:18",
     "trade_no": "xxxx",
     "fund_bill_list": "[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
     "app_id": "xxxx",
     "notify_time": "2016-11-16 11:42:19",
     "point_amount": "0.00",
     "total_amount": "0.01",
     "notify_type": "trade_status_sync",
     "out_trade_no": "xxxx",
     "buyer_logon_id": "xxxx",
     "notify_id": "xxxx",
     "seller_email": "xxxx",
     "receipt_amount": "0.01",
     "invoice_amount": "0.01",
     "sign": "xxx"
}
signature = data.pop("sign")
success = alipay.verify(data, signature)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED" ):
    print("trade succeed")

alipay.trade.pay

alipay = AliPay(appid="", ...)

result = alipay.api_alipay_trade_pay(
    out_trade_no="out_trade_no",
    scene="bar_code/wave_code",
    auth_code="auth_code",
    subject="subject",
    discountable_amount=10,
    total_amount=20,
    notify_url="https://example.com/notify" # this is optional
)

if  result["code"] == "10000":
    print("Order is paid")

alipay.trade.precreate

alipay = AliPay(appid="", ...)

# create an order
alipay.api_alipay_trade_precreate    (
    subject="test subject",
    out_trade_no="out_trade_no",
    total_amount=100
)

# check order status
paid = False
for i in range(10):
    # check every 3s, and 10 times in all
    print("now sleep 3s")
    time.sleep(3)
    result = alipay.api_alipay_trade_query(out_trade_no="out_trade_no")
    if result.get("trade_status", "") == "TRADE_SUCCESS":
        paid = True
        break
    print("not paid...")

# order is not paid in 30s , cancel this order
if paid is False:
    alipay.api_alipay_trade_cancel(out_trade_no=out_trade_no)

alipay.trade.refund

If you want to know what parameters are accepted, take a look into the official document

result = alipay.api_alipay_trade_refund(out_trade_no="xxx", refund_amount="xxx", ...)

if result["code"] == "10000":
    print("success")

alipay.trade.fastpay.refund.query

result = alipay.api_alipay_trade_fastpay_refund_query("20171120", out_trade_no="20171120")

result = {
    'code': '10000',
    'msg': 'Success',
    'out_request_no': '20171120',
    'out_trade_no': '20171120',
    'refund_amount': '20.00',
    'total_amount': '20.00',
    'trade_no': '2017112021001004070200297107'
}

alipay.trade.order.settle

result = alipay.api_alipay_trade_order_settle(
    out_request_no,
    trade_no,
    royalty_parameters
)

alipay.fund.trans.toaccount.transfer

# transfer money to alipay account
result = alipay.api_alipay_fund_trans_toaccount_transfer(
    datetime.now().strftime("%Y%m%d%H%M%S"),
    payee_type="ALIPAY_LOGONID/ALIPAY_USERID",
    payee_account="csqnji8117@sandbox.com",
    amount=3.12
)
result = {'code': '10000', 'msg': 'Success', 'order_id': '', 'out_biz_no': '', 'pay_date': '2017-06-26 14:36:25'}

alipay.fund.trans.order.query

result = alipay.api_alipay_fund_trans_order_query(
    out_biz_no="20170626152216"
)
print(result)

ISV Integration

Go through the details before you do anything, or it may pains.

alipay.open.auth.token.app

isv_alipay = ISVAliPay(
    ...
    app_auth_code="app_auth_code"
)

response = isv_alipay.api_alipay_open_auth_token_app()
response = {
    "code": "10000",
    "msg": "Success",
    "app_auth_token": "201708xxx",
    "app_refresh_token": "201708xxx",
    "auth_app_id": "appid",
    "expires_in": 31536000,
    "re_expires_in": 32140800,
    "user_id": "2088xxxxx
}

alipay.open.auth.token.app.query

response = isv_alipay.alipay_open_auth_token_app_query()

Test

python -m unittest discover

Or you may do test manually in this way, debug=True will direct your request to sandbox environment:

alipay = AliPay(..., debug=True)

Changelog

2018-08-23(version 1.8)

  • alipay.trade.order.settle

2018-03-16(version 1.7)

  • Do encryption/decryption with pycryptodomex,which has not conflict with Pycrypto (many thanks to fakepoet)

2018-01-23(version 1.6)

  • initialize Alipay instance with key string

2017-12-04(version 1.5.1)

  • bug fix for ISVAlipay.build_body.

2017-11-20(version 1.5)

  • alipay.trade.fastpay.refund.query Query refund result.

2017-11-14(version 1.4.1)

  • bug fix for api_alipay_trade_precreate, notify url is not included in request params.

2017-10-20(version 1.4.0)

  • change dependence from pycrypto to pycryptodome. for users upgraded from 1.3.0, uninstall pycrypto first

2017-08-21(version 1.3.0)

  • allow notify_url be overriden in hose 4 functions: alipay.trade.wap.pay alipay_trade_app_pay alipay.trade.page.pay alipay.trade.pay

2017-08-07(version 1.2.0)

  • ISV integration

2017-06-25(version 1.1.0)

  • alipay.fund.trans.toaccount.transfer and alipay.fund.trans.order.query

2017-05-28(version 1.0.1)

  • return url missing for alipay.trade.page.pay

2017-05-26(version 1.0.0)

  • code refactoring, all functions are renamed
  • alipay.trade.page.pay is used instead of create_direct_pay_by_user
  • load key into memory, local key file access is needed for the 1st time