Prerequisites
Before implementing the payment integration, ensure you have the required Python package installed:
pip install python-alipay-sdk
RSA Key Generation
Alipay requires RSA key pairs for secure communication. Follow these steps to generate the necessary keys using OpenSSL.
Generating the Private Key
openssl genrsa -out app_private_key.pem 2048
Exit the OpenSSL prompt by pressing Ctrl+D. Verify the file was created:
cat app_private_key.pem
Generating the Public Key
rsa -in app_private_key.pem -pubout -out app_public_key.pem
Key Configuration Strategy
Store your private key securely within your application codebase. Upload your public key to the Alipay merchant dashboard under the sandbox application settings.
Extract your public key content:
cat app_public_key.pem
The output will resemble:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtL8j5quexGUn5dGTdO76
vx+yfkpOQFkTymk1FALj0FSWucrM7u8+8O5DJtbRI+Skt9tGRNU/ZjG6IlQUBzmM
xIB3b0I3I5GCg2ZaFWmqblzcqo3RZ9aC+kOX9h3o/xeaq5aemwRsPxezJoFmF38f
6YwR8YIWWnqsFw93MWahbeSt02qnZPwKnq41zUSV/iPogUubLud2D7Dg+cgREfm8
pflbTL4utt41PU7O+tbGUet9fQKpliTESs7Gda/IMZf9KtbBKQCjxiVKiLHcMQje
0FcaaYWtyEebE02E4qIqnHRUklKExj1/mQfXQsum0wO6+EQPuN9VSQUaAMSfqQiq
Creating the Alipay Public Key File
Create a file named alipay_public_key.pem in your orders application directory. Include the header and footer markers:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzkXJrjPbdu2DdCygEChuLzuBq0Hhvp2SOoe4LzPR0LyKcQF3TM/5O/K5YlNuspeZgMqm+mNLmpp6ahfo6RrMSrnZ9f5jN81mz7ZAIe7PAG0Fj1lTzkBNLu2Ab2NVgkHT9wf/Qgug+Vef4bSVyVdED9cCxsZq76BdSKHKoSufts1YK8QzEg7oX4f/FcRyo1afuqXl2HV+LSTstw0nLq9VkaOawP5bewTg4L7yIIjsb+RLDO7mwTOe3HoGxmWOTU+EIJk2AWqaQWAIGpRQrQZ54T/B8K0wcuGsTt6Ru5Z2XcGvZ6Mk1drQsZ6u1AuOPIvlR7FM8+azGbQmADLevseYOwIDAQAB
-----END PUBLIC KEY-----
Order Model Configuration
Define enumeration constants in your OrderInfo model to manage order states and payment methods:
class OrderInfo(models.Model):
ORDER_STATUS_ENUM = {
"UNPAID": 1,
"UNSEND": 2,
"UNRECEIVED": 3,
"UNCOMMENT": 4,
"FINISHED": 5
}
PAY_METHODS_ENUM = {
"CASH": 1,
"ALIPAY": 2
}
Django Settings Configuration
Add the Alipay configuration to your settings file:
# Alipay Configuration
ALIPAY_URL = "https://openapi.alipaydev.com/gateway.do"
ALIPAY_APPID = "2016081600258081"
Payment Initialization Endpoint
Frontend JavaScript Implementation
The frontend triggers a payment request when users click the payment button. The request is only sent for orders in the "unpaid" status:
<script type="text/javascript">
$(".oper_btn").click(function() {
const orderId = $(this).attr("order_id");
const orderStatus = parseInt($(this).attr("order_status"));
if (orderStatus === 1) {
$.post("/orders/pay", {
order_id: orderId,
csrfmiddlewaretoken: "{{ csrf_token }}"
}, function(response) {
if (response.code === 1) {
window.location.href = "/users/login";
} else if (response.code === 0) {
window.open(response.url);
} else {
alert(response.message);
}
});
}
});
</script>
Backend Payment View
The payment view handles the order validation and constructs the Alipay payment URL:
import os
from django.conf import settings
from django.http import JsonResponse
from django.views import View
from alipay import AliPay
from .models import OrderInfo
class PayView(LoginRequiredJsonMixin, View):
def post(self, request):
order_id = request.POST.get("order_id")
if not order_id:
return JsonResponse({"code": 2, "message": "Missing order ID"})
try:
order = OrderInfo.objects.get(
order_id=order_id,
user=request.user,
status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"],
pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"]
)
except OrderInfo.DoesNotExist:
return JsonResponse({"code": 3, "message": "Invalid order"})
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None,
app_private_key_path=os.path.join(
settings.BASE_DIR, "apps/orders/app_private_key.pem"
),
alipay_public_key_path=os.path.join(
settings.BASE_DIR, "apps/orders/alipay_public_key.pem"
),
sign_type="RSA2",
debug=True
)
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order_id,
total_amount=str(order.total_amount),
subject=f"Order {order_id}",
return_url=None,
notify_url=None
)
alipay_url = f"{settings.ALIPAY_URL}?{order_string}"
return JsonResponse({"code": 0, "message": "Payment initiated", "url": alipay_url})
URL Routing
url(r"^pay$", views.PayView.as_view(), name="pay"),
Verifying Payment Status
Front end Status Check
After the payment page opens, the frontend polls the backend to verify the transaction outcome:
<script type="text/javascript">
$(".oper_btn").click(function() {
const orderId = $(this).attr("order_id");
const orderStatus = parseInt($(this).attr("order_status"));
if (orderStatus === 1) {
$.post("/orders/pay", {
order_id: orderId,
csrfmiddlewaretoken: "{{ csrf_token }}"
}, function(response) {
if (response.code === 1) {
window.location.href = "/users/login";
} else if (response.code === 0) {
window.open(response.url);
$.get("/orders/check_pay", {order_id: orderId}, function(data) {
if (data.code === 0) {
window.location.reload();
} else {
alert(data.message);
}
});
} else {
alert(response.message);
}
});
}
});
</script>
Backend Verification View
The verification endpoint continuously polls Alipay until a definitive result is obtained:
import time
from django.utils.decorators import method_decorator
class CheckPayStatusView(LoginRequiredJsonMixin, View):
def get(self, request):
order_id = request.GET.get("order_id")
if not order_id:
return JsonResponse({"code": 2, "message": "Missing order ID"})
try:
order = OrderInfo.objects.get(
order_id=order_id,
user=request.user,
status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"],
pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"]
)
except OrderInfo.DoesNotExist:
return JsonResponse({"code": 3, "message": "Invalid order"})
alipay = AliPay(
appid=settings.ALIPAY_APPID,
app_notify_url=None,
app_private_key_path=os.path.join(
settings.BASE_DIR, "apps/orders/app_private_key.pem"
),
alipay_public_key_path=os.path.join(
settings.BASE_DIR, "apps/orders/alipay_public_key.pem"
),
sign_type="RSA2",
debug=True
)
while True:
response = alipay.api_alipay_trade_query(order_id)
code = response.get("code")
trade_status = response.get("trade_status")
if code == "10000" and trade_status == "TRADE_SUCCESS":
order.trade_id = response.get("trade_no")
order.status = OrderInfo.ORDER_STATUS_ENUM["UNCOMMENT"]
order.save()
return JsonResponse({"code": 0, "message": "Payment successful"})
elif code == "40004" or (code == "10000" and trade_status == "WAIT_BUYER_PAY"):
time.sleep(10)
continue
else:
return JsonResponse({"code": 4, "message": "Payment failed"})
URL Routing for Verification
url(r"^check_pay$", views.CheckPayStatusView.as_view(), name="check_pay"),
Integration Notes
The sandbox anvironment (openapi.alipaydev.com) allows testing without real transacsions. Switch to the production gateway URL when deploying to a live environment.
The payment verification loop includes a 10-second delay between queries to avoid hitting rate limits. Consider implementing exponential backoff for production use.