I have always wanted to record the transactions used in the project and the payment function of Alipay. I have been lazy all the time. I will take a look at this while I am a bit interested today.
The essential function of the mall project is to pay the order, so it will involve the preservation of the order and the introduction of the payment interface. At the beginning of the database model, the order is divided into two tables, one is the order form, recording the basic information of the order, such as order number, user information, freight and so on, and the other is the order commodity form, recording the commodity information in the order. When saving an order, it will certainly involve the creation and preservation of two tables. In fact, there is also a table that needs some modifications. That is the commodity table. When an order is successfully saved, it means that the transaction is successful. When the commodity is sold, the inventory of the commodity should be modified. So there are three tables involved in the operation of saving orders. Therefore, when saving an order, the modification of multi-table data should succeed or fail at the same time, which is very similar to the transactions in the database. Therefore, transaction is introduced here to complete the function of order saving.
In Django, a transaction can be defined by atomic provided by the django.db.transaction module. atomic provides two usages, one is decorator, the other is with statement.
from django.db import transaction @transaction.atomic def viewfunc(request): # This code is executed in a transaction ...
from django.db import transaction def viewfunc(request): # This part of the code is not in the transaction, will be Django Automatic submission ... with transaction.atomic(): # This part of the code is executed in the transaction ...
In Django, savepoint support is also provided, savepoint can be created in transactions to record the specific state of data, database error can be restored to the state of data savepoint.
from django.db import transaction # Create savepoints save_id = transaction.savepoint() # Roll back to savepoint transaction.savepoint_rollback(save_id) # Submit all database transaction operations from savepoint to current state transaction.savepoint_commit(save_id)
So, in the Create method of serializer, you can create a transaction, modify and save the data, and create new ones. If something goes wrong, you can roll back directly and commit the transaction if there is no problem. The code is as follows
def create(self, validated_data):
"""
//Save the order
"""
# Get the current single user
user = self.context['request'].user
# Organizational Order No. 20170903153611+user.id
# timezone.now() -> datetime
order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
address = validated_data['address']
pay_method = validated_data['pay_method']
# Generating orders
with transaction.atomic():
# Create a savepoint
save_id = transaction.savepoint()
try:
# Create order information
order = OrderInfo.objects.create(
order_id=order_id,
user=user,
address=address,
total_count=0,
total_amount=Decimal(0),
freight=Decimal(10),
pay_method=pay_method,
status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID']
)
# Get shopping cart information
redis_conn = get_redis_connection("cart")
redis_cart = redis_conn.hgetall("cart_%s" % user.id)
cart_selected = redis_conn.smembers('cart_selected_%s' % user.id)
# Convert bytes type to int type
cart = {}
for sku_id in cart_selected:
cart[int(sku_id)] = int(redis_cart[sku_id])
# # Query out all commodity data at once
# skus = SKU.objects.filter(id__in=cart.keys())
# Processing Order Goods
sku_id_list = cart.keys()
for sku_id in sku_id_list:
while True:
sku = SKU.objects.get(id=sku_id)
sku_count = cart[sku.id]
# Judging Inventory
origin_stock = sku.stock # Original stock
origin_sales = sku.sales # Original sales
if sku_count > origin_stock:
transaction.savepoint_rollback(save_id)
raise serializers.ValidationError('Insufficient stock of goods')
# Used for demonstrating concurrent orders
# import time
# time.sleep(5)
# Reduce stock
# sku.stock -= sku_count
# sku.sales += sku_count
# sku.save()
new_stock = origin_stock - sku_count
new_sales = origin_sales + sku_count
# Update according to the original inventory condition, return the number of items updated, optimistic lock
ret = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
if ret == 0:
continue
# Accumulated Sales Information of SPU s for Commodities
sku.goods.sales += sku_count
sku.goods.save()
# Data for accumulating basic order information
order.total_count += sku_count # Cumulative total amount
order.total_amount += (sku.price * sku_count) # Cumulative total
# Keep the order merchandise
OrderGoods.objects.create(
order=order,
sku=sku,
count=sku_count,
price=sku.price,
)
# Update Successful
break
# Update order amount information
order.total_amount += order.freight
order.save()
except serializers.ValidationError:
raise
except Exception as e:
logger.error(e)
transaction.savepoint_rollback(save_id)
raise
# Submission transaction
transaction.savepoint_commit(save_id)
# Update the shopping cart data saved in redis
pl = redis_conn.pipeline()
pl.hdel('cart_%s' % user.id, *cart_selected)
pl.srem('cart_selected_%s' % user.id, *cart_selected)
pl.execute()
return order
Another point to note is that when an order is submitted, the corresponding goods in the shopping cart should be deleted. All right, that's the transaction in django.
Let's talk about the introduction of Alipay's payment function. Now basically all projects involve third party payment when it comes to payment function. The most widely used ones should be Alipay and WeChat. I use Alipay payment here. When the order is created, the next step is to pay.
Alipay development platform web site https://open.alipay.com/platform/home.htm . Because the developer account needs to be audited, Alipay has provided sandbox environment for testing. Sandbox applications: https://docs.open.alipay.com/200/105311 . Sandbox account: https://openhome.alipay.com/platform/appDaily.htm?tab=account . python docking Alipay SDK: https://github.com/fzlee/alipay/blob/master/README.zh-hans.md. SDK: https://docs.open.alipay.com/270/106291/.
First, flow the process. The user clicks the button to request the Alipay payment interface. First, log in. After the login is successful, the payment operation will be carried out. The successful payment will be callback.
First, the first step is to click on the button. The url will be spliced back to the front end. The url will be returned to the front end, the front end will jump, jump to the Alipay related interface, and the user will login and pay.
Looking at pythonsdk, we can first generate a key (public key and private key) by using the openssl command. The private key is kept by itself and checked by the public key user. The order is as follows:
openssl OpenSSL > genrsa-out app_private_key. PEM 2048 # private key OpenSSL > RSA - in app_private_key. PEM - pubout - out app_public_key. PEM export public key OpenSSL> exit
At the same time, you need to get a public key string from Alipay. https://github.com/fzlee/alipay/blob/master/tests/certs/ali/ali_public_key.pem
Since we use the RSA generated key above, we also need RSA's public key in Alipay.
With the key set, we can start to write the view. The code is as follows:
class PaymentView(APIView): """ //payment """ permission_classes = (IsAuthenticated,) def get(self, request, order_id): """ //Get Payment Links """ # Determine whether the order information is correct try: order = OrderInfo.objects.get(order_id=order_id, user=request.user, pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"], status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"]) except OrderInfo.DoesNotExist: return Response({'message': 'Error in order information'}, status=status.HTTP_400_BAD_REQUEST) # Constructing Alipay payment link address alipay = AliPay( appid=settings.ALIPAY_APPID, app_notify_url=None, # Default callback url app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"), alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"), # Alipay's public key, which verifies the use of Alipay return message, is not your own public key., sign_type="RSA2", # RSA perhaps RSA2 debug=settings.ALIPAY_DEBUG # default False ) order_string = alipay.api_alipay_trade_page_pay( out_trade_no=order_id, total_amount=str(order.total_amount), subject="Medo Mall%s" % order_id, return_url="http://www.meiduo.site:8080/pay_success.html", ) # Need to jump to https://openapi.alipay.com/gateway.do? + order_string # Stitching links back to the front end alipay_url = settings.ALIPAY_URL + "?" + order_string return Response({'alipay_url': alipay_url})
Relevant parameters can be configured in the configuration file (ALPAY_APPID, ALPAY_URL, ALPAY_DEBUG) beforehand to enable the sandbox environment when ALPAY is True. When the payment is successful, the callback address you filled in will be called back. The parameters returned are as follows.
The front-end page sends this data to the back-end, which checks and saves the payment results. This is the whole process. The specific process can be referred to pythonsdk.
I QQ: 595395786, welcome to exchange!!