Setting Up Alipay Sandbox Environment
-
Create an Application Log in to the Ant Financial Open Platform and navigate to the Management Center -> Application List.
https://open.alipay.com/platform/home.htmAfter creating the application, you will receive an
appid. Note that production Alipay payments require enterprise verification, so we use the sandbox environment for development and testing. -
Sandbox Environment Access the sandbox application page at:
https://openhome.alipay.com/platform/appDaily.htm?tab=info -
Generate Public and Private Keys Follow the official guide at https://docs.open.alipay.com/291/105971/ to generate RSA2 keys using the provided tool. For Windows, use the Windows version; for Linux, use the Linux version.
-
Place Key Files
- Copy the generated private key to
trade/keys/private_2048.txtand add the following header and footer:-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY----- - Copy the Alipay public key to
trade/keys/alipay_key_2048.txtand add similar header/footer.
- Copy the generated private key to
Alipay API Documentation
We use the Computer Website Payment API. The main documentation is at https://docs.open.alipay.com/270. Specifically, we use the alipay.trade.page.pay interface.
Important Parameters
sign: Signature. See https://docs.open.alipay.com/291/106118 for details on signing.- Required request parameters include
app_id,method,charset,sign_type,timestamp,version,biz_content.
Implementing the Payment Flow
-
Install Required Module
pip install pycryptodome -
Create the Alipay Utility Class Create
utils/alipay.pywith the following code:
# -*- coding: utf-8 -*-
from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import b64encode, b64decode
from urllib.parse import quote_plus
from urllib.parse import urlparse, parse_qs
from urllib.request import urlopen
from base64 import decodebytes, encodebytes
import json
class AliPay(object):
"""
Alipay payment interface
"""
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.import_key(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",
}
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"))
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)
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))
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)
- Test the Payment URL Run the script to generate a payment URL and test with sandbox account credentials.
Integrating with Django
-
Confgiure URLs
url('alipay/return/', AlipayView.as_view()) -
Update alipay.py with Remote URLs Set
return_urlandapp_notify_urlto the server's endpoitns, e.g.:return_url="http://your-server-ip:8000/alipay/return/" app_notify_url="http://your-server-ip:8000/alipay/return/" -
Add Key Paths to Settings In
settings.py:private_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/private_2048.txt') ali_pub_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/alipay_key_2048.txt') -
Implement the Alipay View In
trade/views.py:
from datetime import datetime
from utils.alipay import AliPay
from rest_framework.views import APIView
from MxShop.settings import ali_pub_key_path, private_key_path
from rest_framework.response import Response
from django.shortcuts import redirect
class AlipayView(APIView):
def get(self, request):
"""Handle Alipay return_url"""
processed_dict = {}
for key, value in request.GET.items():
processed_dict[key] = value
sign = processed_dict.pop("sign", None)
alipay = AliPay(
appid="2016090900469819",
app_notify_url="http://your-server-ip:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path,
debug=True,
return_url="http://your-server-ip:8000/alipay/return/"
)
verify_re = alipay.verify(processed_dict, sign)
if verify_re is True:
response = redirect("index")
response.set_cookie("nextPath", "pay", max_age=3)
return response
else:
response = redirect("index")
return response
def post(self, request):
"""Handle Alipay notify_url"""
processed_dict = {}
for key, value in request.POST.items():
processed_dict[key] = value
sign = processed_dict.pop("sign", None)
alipay = AliPay(
appid="2016090900469819",
app_notify_url="http://your-server-ip:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path,
debug=True,
return_url="http://your-server-ip:8000/alipay/return/"
)
verify_re = alipay.verify(processed_dict, sign)
if verify_re is True:
order_sn = processed_dict.get('out_trade_no', None)
trade_no = processed_dict.get('trade_no', None)
trade_status = processed_dict.get('trade_status', None)
existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
for existed_order in existed_orders:
order_goods = existed_order.goods.all()
for order_good in order_goods:
goods = order_good.goods
goods.sold_num += order_good.goods_num
goods.save()
existed_order.pay_status = trade_status
existed_order.trade_no = trade_no
existed_order.pay_time = datetime.now()
existed_order.save()
return Response("success")
- Add Payment URL to Serializers
In
trade/serializers.py, add aSerializerMethodFieldto generate the Alipay payment URL:
class OrderSerializer(serializers.ModelSerializer):
alipay_url = serializers.SerializerMethodField(read_only=True)
def get_alipay_url(self, obj):
alipay = AliPay(
appid="2016090900469819",
app_notify_url="http://your-server-ip:8000/alipay/return/",
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path,
debug=True,
return_url="http://your-server-ip:8000/alipay/return/"
)
url = alipay.direct_pay(
subject=obj.order_sn,
out_trade_no=obj.order_sn,
total_amount=obj.order_mount,
)
re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
return re_url
Deploying Vue Static Files with Django
-
Build Vue Project Run
cnpm run buildto generate static files in thedistdirectory. -
Copy Files
- Copy
index.htmlto Django'stemplatesdirectory. - Copy
dist/staticcontents to Django'sstaticdirectory. - Copy
index.entry.jsto the Djangostaticdirectory.
- Copy
-
Configure Static Files in settings.py
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), ) -
Update index.html Change the script source to:
<script type="text/javascript" src="/static/index.entry.js"></script> -
Configure URL for Index In
urls.py:from django.views.generic import TemplateView urlpatterns = [ url(r'^index/', TemplateView.as_view(template_name="index.html"), name="index"), ] -
Update the Return URL Redirect Ensure the
AlipayView.getmethod redirects appropriately.
After deploying these changes, the application should be accessible via the index URL. Users can add products to the cart, proceed to checkout, create orders, and be redirected to the Alipay sandbox payment page.