浏览代码

PayPal支付宝微信退款

locky 4 年之前
父节点
当前提交
27dcf61e48
共有 3 个文件被更改,包括 76 次插入8 次删除
  1. 66 2
      Controller/CloudStorage.py
  2. 4 4
      Model/models.py
  3. 6 2
      Object/ResponseObject.py

+ 66 - 2
Controller/CloudStorage.py

@@ -109,6 +109,8 @@ class CloudStorageView(View):
             return self.do_vod_msg_end(request_dict)
         elif operation == 'vodMsgNotice':  # 云存操作系统消息
             return self.do_vod_msg_Notice(request_dict)
+        elif operation == 'doRefund':  # 退款
+            return self.do_refund(request_dict, response)
 
         else:
             token = request_dict.get('token', None)
@@ -818,6 +820,7 @@ class CloudStorageView(View):
             paypalrestsdk.configure(PAYPAL_CRD)
             # ID of the payment. This ID is provided when creating payment.
             payment = paypalrestsdk.Payment.find(paymentId)
+            print(payment)
             payres = payment.execute({"payer_id": PayerID})
             print(payres)
             if not payres:
@@ -1065,7 +1068,6 @@ class CloudStorageView(View):
 
         orderID = CommonService.createOrderID()
         if pay_type == 1:
-
             cal_url = "{SERVER_DOMAIN_SSL}web/paid2/fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
             if lang != 'cn':
                 cal_url = "{SERVER_DOMAIN_SSL}web/paid2/en_fail.html".format(SERVER_DOMAIN_SSL=SERVER_DOMAIN_SSL)
@@ -1090,6 +1092,7 @@ class CloudStorageView(View):
                 print(payment.error)
                 return response.json(10, payment.error)
             print(payment)
+            paymentID = payment['id']   # 获取paymentID
             for link in payment.links:
                 if link.rel == "approval_url":
                     approval_url = str(link.href)
@@ -1099,7 +1102,7 @@ class CloudStorageView(View):
                                                price=price, currency=currency, addTime=nowTime, updTime=nowTime,
                                                pay_url=approval_url, isSelectDiscounts=is_select_discount,
                                                commodity_code=commodity_code, commodity_type=commodity_type,
-                                               rank_id=rank)
+                                               rank_id=rank, paymentID=paymentID)
                     return response.json(0, {"redirectUrl": approval_url, "orderID": orderID})
             return response.json(10, 'generate_order_false')
         elif pay_type == 2:
@@ -1668,6 +1671,67 @@ class CloudStorageView(View):
             res = aliSms.send_code_sms_cloud(phone=phone, params = params, sign_name=sign_ms,
                                        temp_msg=temp_msg)
 
+    def do_refund(self, request_dict, response):
+        orderID = request_dict.get('orderID', None)    # 商户订单号
+        if not orderID:
+            return response.json(444)
+        try:
+            order_qs = Order_Model.objects.filter(orderID=orderID).\
+                values('status', 'payType', 'price', 'currency', 'paymentID')
+            if not order_qs.exists():
+                return response.json(173)
+
+            # 支付状态不为支付成功和退款失败
+            status = order_qs[0]['status']
+            if status != 1 and status != 4:
+                return response.json(805)
+
+            now_time = int(time.time())
+            payType = order_qs[0]['payType']
+            refund_amount = order_qs[0]['price']    # 退款金额
+            currency = order_qs[0]['currency']      # 货币
+            paymentID = order_qs[0]['paymentID']    # 退款id
+            out_request_no = str(time.strftime('%Y%m%d%H%M%S', time.localtime(now_time)))    # 退款请求号
+            # 根据支付类型处理退款
+            if payType == 1:    # PayPal
+                paypalrestsdk.configure(PAYPAL_CRD)
+                payment = paypalrestsdk.Payment.find(paymentID)
+                print(payment)
+                related_resources = payment['transactions'][0]['related_resources']
+                if not related_resources:
+                    return response.json(805)
+                sale = related_resources[0]['sale']
+                if sale['state'] != 'completed':
+                    return response.json(805)
+                sale_id = sale['id']
+                paypalSale = paypalrestsdk.Sale.find(sale_id)
+                refund = paypalSale.refund({
+                    'amount': {
+                        'total': refund_amount,
+                        'currency': currency
+                    }
+                })
+                status = 5 if refund.success() else 4
+            elif payType == 2:  # 支付宝
+                aliPayObj = AliPayObject()
+                alipay = aliPayObj.conf()
+                refund_response = alipay.api_alipay_trade_refund(refund_amount=refund_amount, out_trade_no=orderID,
+                                                                 out_request_no=out_request_no)
+                # 退款成功,修改订单支付状态为'退款成功',否则改为'退款失败'
+                status = 5 if refund_response['code'] == '10000' else 4
+            elif payType == 3:  # 微信
+                wechatPayObj = WechatPayObject()
+                refund_amount = int(float(refund_amount) * 100)  # 退款金额,单位为分,只能为整数
+                refund_success = wechatPayObj.refund(out_trade_no=orderID, out_refund_no=out_request_no,
+                                                     total_fee=refund_amount, refund_fee=refund_amount)
+                status = 5 if refund_success else 4
+            else:   # 不支持退款的支付类型
+                return response.json(805)
+            order_qs.update(status=status, updTime=now_time)    # 更新订单状态
+            return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(500, repr(e))
 
 
 # 删除过期云存播放列表

+ 4 - 4
Model/models.py

@@ -569,9 +569,9 @@ class App_Colophon(models.Model):
 
 class Order_Model(models.Model):
     orderID = models.CharField(blank=True, max_length=20, primary_key=True, verbose_name=u'订单id')
+    paymentID = models.CharField(blank=True, max_length=64, default='', verbose_name='付款id')
     trade_no = models.CharField(blank=True, max_length=32, default='', verbose_name='第三方订单号')
-    # 订单关联用户
-    userID = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE)
+    userID = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE)    # 订单关联用户
     UID = models.CharField(max_length=20, verbose_name='设备UID')
     channel = models.SmallIntegerField(default=0, verbose_name=u'通道数')
     desc = models.CharField(max_length=50, default='', verbose_name='商品描述')
@@ -579,9 +579,9 @@ class Order_Model(models.Model):
     currency = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'货币')
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     updTime = models.IntegerField(verbose_name='更新时间', default=0)
-    # endTime = models.IntegerField(verbose_name='结束时间', default=0)
     isSelectDiscounts = models.SmallIntegerField(default=0, verbose_name=u'用户是否选择了第二年优惠 [0=否,1是]')
-    status = models.SmallIntegerField(default=0, verbose_name='付款状态')  # 0:待支付,1:成功,2:取消,3:已退款, 9:处理中,10:付款失败
+    # 0: 待支付, 1:成功, 2: 取消, 4: 退款失败, 5: 退款成功, 9:处理中, 10:付款失败
+    status = models.SmallIntegerField(default=0, verbose_name='付款状态')
     payType = models.SmallIntegerField(default=0, verbose_name='付款类型0:paypal,1:alipay')
     payTime = models.IntegerField(verbose_name='支付成功时间', default=0)
     rank = models.ForeignKey(Store_Meal, to_field='id', default='', on_delete=models.CASCADE, verbose_name='套餐类型')

+ 6 - 2
Object/ResponseObject.py

@@ -61,6 +61,7 @@ class ResponseObject(object):
             801: 'The refund amount cannot be greater than the order price',
             802: 'The order has been completed and cannot be cancelled',
             804: 'Refund, please do not repeat the operation',
+            805: 'No refund',
             900: 'There is no information about this version!',
             901: 'Getting URL failure!',
             902: 'No update!',
@@ -96,8 +97,9 @@ class ResponseObject(object):
             10040: 'Invalid activation code',
             10041: 'This device has purchased a domestic cloud storage package, and cannot purchase a foreign cloud storage package',
             10042: 'The device has registered a certificate',
-            10043: 'The device does not have a serial number',
+            10043: 'The device does not registered a certificate',
             10044: 'Request to publish MQTT topic message failed',
+            10045: 'Already the latest version',
         }
         data_cn = {
             0: '成功',
@@ -153,6 +155,7 @@ class ResponseObject(object):
             801: '退款金额不能大于订单价格',
             802: '订单已完成,不能撤销',
             804: '订单已退款,请勿重复操作',
+            805: '无法退款',
             900: '版本信息不存在',
             901: '获取链接失败',
             902: '无更新!',
@@ -188,8 +191,9 @@ class ResponseObject(object):
             10040: '无效激活码',
             10041: '此设备已购买过国内云存套餐,无法购买国外云存套餐',
             10042: '此设备已注册证书',
-            10043: '此设备没有序列号',
+            10043: '此设备没有注册证书',
             10044: '请求发布MQTT主题消息失败',
+            10045: '当前为最新版本',
         }
         if self.lang == 'cn':
             msg = data_cn