فهرست منبع

新增联通套餐支付API

zhangdongming 3 سال پیش
والد
کامیت
f8c8029739

+ 2 - 1
Ansjer/server_urls/unicom_url.py

@@ -8,8 +8,9 @@
 """
 from django.urls import re_path
 
-from Controller.UnicomCombo import UnicomComboController
+from Controller.UnicomCombo import UnicomComboController, UnicomComboPayNotifyController
 
 urlpatterns = [
     re_path(r'^api/(?P<operation>.*)$', UnicomComboController.UnicomComboView.as_view()),
+    re_path(r'^wap/pay/(?P<operation>.*)$', UnicomComboPayNotifyController.UnicomComboPayNotifyView.as_view()),
 ]

+ 65 - 3
Controller/UnicomCombo/UnicomComboController.py

@@ -14,10 +14,12 @@ from django.db import transaction
 from django.http import HttpResponse
 from django.views.generic.base import View
 
-from Model.models import UnicomDeviceInfo, UnicomCombo, Pay_Type
+from Model.models import UnicomDeviceInfo, UnicomCombo, Pay_Type, Order_Model
 from Object.ResponseObject import ResponseObject
 from Object.TokenObject import TokenObject
 from Object.UnicomObject import UnicomObjeect
+from Object.utils.PayUtil import PayService
+from Service.CommonService import CommonService
 
 
 class UnicomComboView(View):
@@ -34,6 +36,10 @@ class UnicomComboView(View):
     def validation(self, request_dict, request, operation):
         if operation == 'buy-notify':
             return self.package_callback_notify(request_dict, request)
+        elif operation == 'alipay-notify':
+            pass
+        elif operation == 'wechat-notify':
+            pass
         elif operation == 'device-queue-monitoring':
             return self.device_queue_monitoring_push(request_dict, request)
         elif operation == 'device-status-change':
@@ -165,12 +171,68 @@ class UnicomComboView(View):
             return response.json(177, repr(e))
 
     @classmethod
-    def buy_package(cls):
+    def buy_package(cls, user_id, request_dict, request, response):
         """
         购买套餐
         @return:
         """
-        pass
+        try:
+            with transaction.atomic():
+                iccid = request_dict.get('iccid', None)
+                combo_id = request_dict.get('id', None)
+                pay_type = request_dict.get('payType', None)
+                if not all([iccid, combo_id, pay_type]):
+                    return response.json(444)
+                combo_id = int(combo_id)
+                pay_type = int(pay_type)
+                now_time = int(time.time())
+                unicom_combo_qs = UnicomCombo.objects.filter(id=combo_id, pay_type=pay_type, is_show=1, is_del=False,
+                                                             status=0) \
+                    .values('id', 'combo_name', 'price', 'remark')
+                if not unicom_combo_qs.exists():
+                    return response.json(173)
+                unicom_device_qs = UnicomDeviceInfo.objects.filter(iccid=iccid) \
+                    .values('serial_no')
+                if not unicom_device_qs.exists():
+                    return response.json(173)
+                unicom_combo_qs = unicom_combo_qs.first()
+                price = unicom_device_qs['price']
+                if not price:
+                    return response.json(173)
+                unicom_device_qs = unicom_device_qs.first()
+                device_uid = CommonService.query_uid_with_serial(unicom_device_qs['serial_no'])
+                order_id = CommonService.createOrderID()
+
+                order_dict = {'orderID': order_id, 'UID': device_uid,
+                              'userID_id': user_id, 'desc': unicom_combo_qs['combo_name'], 'payType': pay_type,
+                              'payTime': now_time, 'price': price, 'currency': 'CNY', 'addTime': now_time,
+                              'updTime': now_time,
+                              'unify_combo_id': str(unicom_combo_qs['id']), 'order_type': 2,
+                              }
+                if pay_type == 2:  # 支付宝
+                    pay_price = PayService.get_two_float(price, 2)
+                    notify_url = 'unicom/wap/pay/ali-notify'
+                    order_dict['pay_url'] = PayService.create_alipay_payment('cn', order_id, pay_price,
+                                                                             unicom_combo_qs.combo_name,
+                                                                             notify_url,
+                                                                             unicom_combo_qs.remark, response)
+                    res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': order_id}
+                elif pay_type == 3:  # 微信支付
+                    notify_url = 'unicom/wap/pay/wechat-notify'
+                    ip = CommonService.get_ip_address(request)
+                    order_dict['pay_url'], sign_params = PayService.create_wechat_payment('cn', order_id, price, ip,
+                                                                                          notify_url,
+                                                                                          unicom_combo_qs.remark,
+                                                                                          response)
+                    res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': order_id, 'result': sign_params}
+                else:
+                    return response.json(444, {'param': 'pay_type'})
+
+                Order_Model.objects.create(**order_dict)
+                return response.json(0, res_data)
+        except Exception as e:
+            print(e)
+            return response.json(500, repr(e))
 
     @classmethod
     def query_device_usage_history(cls):

+ 181 - 0
Controller/UnicomCombo/UnicomComboPayNotifyController.py

@@ -0,0 +1,181 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : UnicomComboPayNotifyController.py
+@Time    : 2022/6/29 14:31
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+import logging
+import time
+from urllib.parse import parse_qs, unquote
+
+from django.db import transaction
+from django.http import HttpResponse
+from django.views import View
+
+from Model.models import Order_Model, UnicomDeviceInfo, UnicomComboOrderInfo
+from Object.AliPayObject import AliPayObject
+from Object.RedisObject import RedisObject
+from Object.WechatPayObject import WechatPayObject
+from Service.CommonService import CommonService
+
+
+class UnicomComboPayNotifyView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        if operation == 'ali-notify':
+            return self.alipay_notify(request_dict)
+        elif operation == 'wechat-notify':
+            return self.wechat_notify(request)
+
+    @classmethod
+    def alipay_notify(cls, request_dict):
+        """
+        支付宝网页支付异步回调通知
+        参考文档 https://opendocs.alipay.com/open/203/105286
+        @param request_dict: 请求参数
+        @return: success or fail
+        """
+        logger = logging.getLogger('info')
+        logger.info('联通套餐支付---支付宝支付回调')
+        order_id = ''
+        redisObj = RedisObject()
+        notify_key = 'ansjer:unicom:alipay:{}:str'
+        try:
+            re_data = request_dict.dict()
+            passback_params = re_data['passback_params']
+            params = dict([(k, v[0]) for k, v in parse_qs(unquote(passback_params)).items()])
+            lang = params['lang']
+            logger.info('支付宝异步回调参数:{},携带参数{}', re_data, lang)
+            signature = re_data['sign']
+            re_data.pop('sign')
+            order_id = re_data['out_trade_no']
+
+            # redis加锁,防止订单重复  命令在指定的 key 不存在时,为 key 设置指定的值。存在则返回0
+            isLock = redisObj.CONN.setnx(notify_key.format(order_id), 1)
+            # 过期时间60秒
+            redisObj.CONN.expire(notify_key.format(order_id), 60)
+            if not isLock:
+                return HttpResponse('fail')
+
+            order_qs = Order_Model.objects.filter(orderID=order_id)
+            if not order_qs.exists():
+                logger.info('系统订单不存在:{}'.format(order_id))
+                return HttpResponse('fail')
+            order_qs = order_qs.filter(status=0)
+            if not order_qs.exists():
+                logger.info('订单已支付或退款{}'.format(order_id))
+                return HttpResponse('success')
+            aliPayObj = AliPayObject()
+            alipay = aliPayObj.conf()
+            # 异步回调验签
+            success = alipay.verify(re_data, signature)
+            if not success or re_data["trade_status"] not in ("TRADE_SUCCESS", "TRADE_FINISHED"):
+                return HttpResponse('fail')
+            return cls.order_pay_notify(order_id, re_data["trade_no"], notify_key.format(order_id), order_qs, redisObj)
+        except Exception as e:
+            logger.info('联通套餐订单支付宝支付回调异常:{}'.format(repr(e)))
+            redisObj.del_data(key=notify_key.format(order_id))
+            return HttpResponse('fail')
+
+    @classmethod
+    def wechat_notify(cls, request):
+        logger = logging.getLogger('info')
+        logger.info('联通套餐支付---微信支付回调')
+        order_id = ''
+        redisObj = RedisObject()
+        notify_key = 'ansjer:unicom:wxpay:{}:str'
+        pay = WechatPayObject()
+        try:
+            re_data = pay.weixinpay_call_back(request.body)
+            attach = re_data["attach"]
+            parmap = dict([(k, v[0]) for k, v in parse_qs(unquote(attach)).items()])
+            lang = parmap['lang']
+            logger.info('微信异步回调参数:{},携带参数{}', re_data, lang)
+            trade_status = re_data['result_code']  # 业务结果  SUCCESS/FAIL
+            order_id = re_data['out_trade_no']  # 商户订单号
+            order_qs = Order_Model.objects.filter(orderID=order_id)
+            if not order_qs.exists():
+                return HttpResponse(pay.xml_to_dict({'return_code': 'FAIL'}))
+            order_qs = order_qs.filter(status=0)
+            if not order_qs.exists():
+                logger.info('订单已支付或退款{}'.format(order_id))
+                return cls.wx_return_code()
+
+            if trade_status != 'SUCCESS':
+                logger.info('微信支付回调Fail')
+                order_qs.update(status=10)
+                return HttpResponse(pay.xml_to_dict({'return_code': 'FAIL'}))
+            check_sign = pay.get_notifypay(re_data)
+            if not check_sign:
+                logger.info('微信支付回调签名失败')
+                return HttpResponse(pay.xml_to_dict({'return_code': 'FAIL', 'return_msg': '签名失败'}))
+
+            # redis加锁,防止订单重复
+            redisObj = RedisObject()
+            isLock = redisObj.CONN.setnx(notify_key.format(order_id), 1)
+            redisObj.CONN.expire(notify_key.format(order_id), 60)
+            if not isLock:
+                return HttpResponse(pay.xml_to_dict({'return_code': 'FAIL'}))
+
+            return cls.order_pay_notify(order_id, re_data["transaction_id"], notify_key.format(order_id), order_qs,
+                                        redisObj, True)
+        except Exception as e:
+            redisObj.del_data(key=notify_key.format(order_id))
+            return HttpResponse(pay.xml_to_dict({'return_code': 'FAIL', 'return_msg': repr(e)}))
+
+    @staticmethod
+    def wx_return_code():
+        return HttpResponse("<xml>\
+                                  <return_code><![CDATA[SUCCESS]]></return_code>\
+                                  <return_msg><![CDATA[OK]]></return_msg>\
+                              </xml>")
+
+    @classmethod
+    def order_pay_notify(cls, order_id, trade_no, request_key, order_qs, redisObj, is_wechat_pay=False):
+        """
+        订单支付通知
+        @param trade_no: 第三方交易流水号
+        @param order_id:订单编号
+        @param request_key:访问缓存key
+        @param order_qs:订单对象
+        @param redisObj:缓存对象
+        @param is_wechat_pay:是否微信支付
+        @return: success or fail
+        """
+        logger = logging.getLogger('info')
+        now_time = int(time.time())
+        with transaction.atomic():
+            # 支付宝交易凭证号。
+            order_qs = order_qs.values('UID', 'unify_combo_id', 'userID_id', 'order_type')
+            device_uid = order_qs[0]['UID']
+            combo_id = order_qs[0]['unify_combo_id']
+            serial_no = CommonService.query_serial_with_uid(device_uid)
+            unicom_device_info_qs = UnicomDeviceInfo.objects.filter(serial_no=serial_no).values('iccid')
+            if unicom_device_info_qs.exists():
+                iccid = unicom_device_info_qs[0]['iccid']
+                combo_order_data = {'iccid': iccid, 'status': 0, 'order_id': order_id, 'combo_id': int(combo_id),
+                                    'user_id': order_qs[0]['userID_id'], 'updated_time': now_time,
+                                    'created_time': now_time}
+                UnicomComboOrderInfo.objects.create(**combo_order_data)
+            order_qs.update(trade_no=trade_no, status=1, payTime=now_time, updTime=now_time)
+            logger.info('购买联通套餐成功,序列号为:{}'.format(serial_no))
+        redisObj.del_data(key=request_key)
+        if is_wechat_pay:
+            return HttpResponse("<xml>\
+                          <return_code><![CDATA[SUCCESS]]></return_code>\
+                          <return_msg><![CDATA[OK]]></return_msg>\
+                        </xml>")
+        else:
+            return HttpResponse('success')

+ 58 - 0
Object/utils/PayUtil.py

@@ -0,0 +1,58 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : PayUtil.py
+@Time    : 2022/6/29 11:41
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+from urllib.parse import quote
+
+from Ansjer.config import SERVER_DOMAIN_SSL
+from Object.AliPayObject import AliPayObject
+from Object.WechatPayObject import WechatPayObject
+
+"""
+支付服务
+"""
+
+
+class PayService:
+
+    @staticmethod
+    def create_alipay_payment(lang, order_id, price, title, notify_url, content, response):
+        aliPayObj = AliPayObject()
+        alipay = aliPayObj.conf()
+        subject = title + content
+        order_string = alipay.api_alipay_trade_wap_pay(
+            out_trade_no=order_id,
+            total_amount=price,
+            subject=subject,
+            return_url="{}web/paid2/success.html".format(SERVER_DOMAIN_SSL),
+            notify_url="{}{}".format(SERVER_DOMAIN_SSL, notify_url),
+            quit_url="{}web/paid2/fail.html".format(SERVER_DOMAIN_SSL),
+            passback_params=quote("lang=" + lang)
+        )
+        if not order_string:
+            return response.json(10, '生成订单错误.')
+        return aliPayObj.alipay_prefix + order_string
+
+    @staticmethod
+    def create_wechat_payment(lang, order_id, price, ip, notify_url, content, response):
+        notify_url = "{}{}".format(SERVER_DOMAIN_SSL, notify_url)
+        pay = WechatPayObject()
+        # 统一调用接口
+        pay.get_parameter(order_id, content, float(price) * 100, ip, notify_url, quote("lang=" + lang))
+        sign_params = pay.re_finall(orderid=order_id)
+        if not sign_params:
+            return response.json(10, '生成订单错误.')
+        return notify_url, sign_params
+
+    @staticmethod
+    def get_two_float(f_str, n):
+        # f_str = '{}'.format(f_str) 也可以转换为字符串
+        f_str = str(f_str)
+        a, b, c = f_str.partition('.')
+        # 如论传入的函数有几位小数,在字符串后面都添加n为小数0
+        c = (c + "0" * n)[:n]
+        return ".".join([a, c])