Kaynağa Gözat

微信支付宝退款

locky 4 yıl önce
ebeveyn
işleme
5bf136463e

+ 23 - 0
Ansjer/file/wechatpay/apiclient_cert.pem

@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID7DCCAtSgAwIBAgIUfF0ljlOH7y//Ggpp9FV3bvBumSwwDQYJKoZIhvcNAQEL
+BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
+FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
+Q0EwHhcNMjEwNzMwMDc1ODU5WhcNMjYwNzI5MDc1ODU5WjB+MRMwEQYDVQQDDAox
+NTA4MjA5NzQxMRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xKjAoBgNVBAsM
+IeePoOa1t+WuieWjq+S9s+eUteWtkOaciemZkOWFrOWPuDELMAkGA1UEBgwCQ04x
+ETAPBgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAy/yNuCQI8pg92b9ptotcNyrHun37o7pJN7dzxVzGXwf2DdDJfrAHsen34m6c
+dNF8foEr8k2gCAdPuHsOU7iPtmyblRXpGJj1MC1kFton4X0zd/+TykcEtfK6o9u3
+KZY1yttt6QEAlFVI+5AzsUHK5Cz0LbH/61udDQyywxqbV0bEHGoxVDq4PmsbL83v
+Y/KE+40tONGRrMvP3DzBhT1afJe8hYFTdCYGGF9hZgQHDn6pmvroicT+32BBqqYz
+tjPIU73SLs+kHB+Fj7D+GhloSJRLfASVsrAfuAsbFQHc+JVmF0RhSjZRoo4oyMmw
+FuWz6adBAGXOgNh8BX5bhkQN9QIDAQABo4GBMH8wCQYDVR0TBAIwADALBgNVHQ8E
+BAMCBPAwZQYDVR0fBF4wXDBaoFigVoZUaHR0cDovL2V2Y2EuaXRydXMuY29tLmNu
+L3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0
+NkMwMUMzRThFQkQyMA0GCSqGSIb3DQEBCwUAA4IBAQCKOhM/GTDLL6pkAmrRdwW0
+gPQAih237SHN8UaXVF/shMLcnC4TQtb74b7T7q93ZANjvJJpkCwBRFvYdTPZZLPG
+Y2+KmwIaekAMNyjE8EXU9Y+IQhAdGyLhWiZk+hYK7qwfhjVfqNa/vgu6GAf+mgad
+gaGdxJHY7F2u2tycwetOPd+fD0PTUWqLvZyL04HFrmtHmuZOxYnGvF8scNDRpDAN
+cgRzZ0oFDa/J1QdZTpY/7HAtp3i+gjlqwypVYujI/oLbpYBzB1Zwf5WX6Qt6Ok66
+m1dJWhCkgB0mAyAys6atlCcLTxAksRIFGuIjoBOqOABsi3X5xFau99PH8bshybKU
+-----END CERTIFICATE-----

+ 28 - 0
Ansjer/file/wechatpay/apiclient_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDL/I24JAjymD3Z
+v2m2i1w3Kse6ffujukk3t3PFXMZfB/YN0Ml+sAex6ffibpx00Xx+gSvyTaAIB0+4
+ew5TuI+2bJuVFekYmPUwLWQW2ifhfTN3/5PKRwS18rqj27cpljXK223pAQCUVUj7
+kDOxQcrkLPQtsf/rW50NDLLDGptXRsQcajFUOrg+axsvze9j8oT7jS040ZGsy8/c
+PMGFPVp8l7yFgVN0JgYYX2FmBAcOfqma+uiJxP7fYEGqpjO2M8hTvdIuz6QcH4WP
+sP4aGWhIlEt8BJWysB+4CxsVAdz4lWYXRGFKNlGijijIybAW5bPpp0EAZc6A2HwF
+fluGRA31AgMBAAECggEAN2/8yAWrFNtBO/NkdILtXeTXgRyKDhYrnePSp9mt49fT
+DDApArAudzDB+JzdEnZ4Yh0S53gnPOdXyChRXXl0PTzJD7xzrAat5BkB8Z/6ZLLV
+ERC5hqjEY/qCtNOatMeo+OenuEJYePewrQWkttDNpjN9bQnKJpmwVgKUdqe9ffs2
+MK15/ZeXUMPXb4U8IER7qr8muVABydxUzefx9F0OBTogbMSQ6q9UZw1cunD91T2H
+lJaQ4erSdLqEj0WEhf6FJ/rWJjfKA7MYYxTKM0YpODr4hf4R2Qds3iHJDIp9Y3Rp
+AlskQaWXEzoJVfYNCglMmTkrlGDSQlr3juaidkjphQKBgQDuY3swj5aUA5ui5S+y
+aFqwDu4OxZC9cVotFii5FuVGQIoylGRvkswPjYYXqZAHKpW+l2muiOJOZ9t8v7D8
+uZKjei5WT6DapnbXOqGbikNEsmnX56jiwB2oAnD41SHzK7jiXEP3rSK2OsXrEQjL
+NqeXAGHYetO7Xip0Tyu9WMUKSwKBgQDbDnG3lEC5TYWh1soq6JVCh9NJY/bOT/eO
+IWimTRlOV1S754auVjOV5FMwlgJN2MtP2qxxSjxjAInEVN9XgYIumh4XofuZ0esk
+IcnfWTpMXzbuT/KpkyjIjN+jwt0cjXbgHClwZpcYtoufGIlija2uuB5JlTeZ3eL/
+E2ovQuggvwKBgDzgarUb9fgWc8hFfsiwnXgYV/KtwSifqceZwQyDLEmkcaY4/uz/
+L91I5tVkF8O8vEDPqc1IZ4JeQ064JRpkyW215d9ktk8f83lDH8H4rPT+Gsln/1jo
+dqpQ9UiWJPVUVQJ5TWISdKrUl3ZQA07hxippjpMZyrguc+j/zsurv6XBAoGBAKYb
+rd+na+0X+91dIrCm7YgltvJP+zc3LGF6tABnNmlRj89tIZPRigx/txWAqv6dBo7n
+IxxFTsVEAzP9p96TNkRgWbFSA5gAtm4PsSTMZIjFKsJLnZc5IBPzvyJPAZxFKX+H
+22SAZtM2mWlrEcLJ7Iow08a4gBENULRb5IkyExPBAoGAQ1BwOxsg4XeKt8aXPLnB
+3Crw4XZAj3rGDSHxXFWXFe4+gwycQ9mGCls4MZyKLoGoiADKNau+DaJ4CGETkpBn
+F8hZ8l4F76RQpH2X+Qhn2hEL0dreeWcpAzC2NLHuthV5bR0EhGNgXwd8NjDC0CXU
+VaSntDOpcfQSRgvp/kjPE64=
+-----END PRIVATE KEY-----

+ 44 - 0
Controller/CloudStorage.py

@@ -108,6 +108,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)
@@ -1671,6 +1673,48 @@ 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')
+            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']    # 退款金额
+            out_request_no = str(time.strftime('%Y%m%d%H%M%S', time.localtime(now_time)))    # 退款请求号
+            # 根据支付类型处理退款
+            if payType == 1:    # PayPal
+                pass
+            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
+                order_qs.update(status=status, updTime=now_time)
+            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
+                order_qs.update(status=status, updTime=now_time)
+            else:   # 不支持退款的支付类型
+                return response.json(805)
+            return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(500, repr(e))
 
 
 # 删除过期云存播放列表

+ 1 - 1
Model/models.py

@@ -581,7 +581,7 @@ class Order_Model(models.Model):
     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:付款失败
+    status = models.SmallIntegerField(default=0, verbose_name='付款状态')  # 0:待支付,1:成功,2:取消,3:已退款,4: 退款失败,5: 退款成功, 9:处理中,10:付款失败
     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='套餐类型')

+ 2 - 0
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!',
@@ -154,6 +155,7 @@ class ResponseObject(object):
             801: '退款金额不能大于订单价格',
             802: '订单已完成,不能撤销',
             804: '订单已退款,请勿重复操作',
+            805: '无法退款',
             900: '版本信息不存在',
             901: '获取链接失败',
             902: '无更新!',

+ 27 - 0
Object/WechatPayObject.py

@@ -5,6 +5,8 @@ from urllib.parse import quote
 import requests
 import xmltodict
 
+from Ansjer.config import BASE_DIR
+
 
 class WechatPayObject:
     """配置账号信息"""
@@ -20,6 +22,7 @@ class WechatPayObject:
         self.TRADE_TYPE = 'APP'
         self.APIKEY = 'ZHansjeransjeransjer680301000000'
         self.url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'  # 微信请求url
+        self.refund_url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'   # 退款url
         self.error = None
         self.params = None
 
@@ -199,3 +202,27 @@ class WechatPayObject:
             xml += '<' + k + '>' + v + '</' + k + '>'
         xml += "</xml>"
         return xml
+
+    def refund(self, out_trade_no, out_refund_no, total_fee, refund_fee):
+        params = {
+            'appid': self.APPID,
+            'mch_id': self.MCHID,
+            'nonce_str': self.getNonceStr(),
+            'out_trade_no': out_trade_no,   # 商户订单号
+            'out_refund_no': out_refund_no,    # 商户退款单号
+            'total_fee': total_fee,     # 订单金额
+            'refund_fee': refund_fee    # 退款金额
+        }
+        # doc: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
+        string_sign = "&".join([f"{k}={params[k]}" for k in sorted(params)] + [f"{'key'}={self.APIKEY}"])
+        params['sign'] = hashlib.md5(string_sign.encode('utf8')).hexdigest().upper()   # MD5签名
+        xml = "<xml>{}</xml>".format("".join([f"<{k}>{v}</{k}>" for k, v in params.items()]))   # 数据拼接成xml格式
+        cert = f"{BASE_DIR}/Ansjer/file/wechatpay/apiclient_cert.pem"
+        key = f"{BASE_DIR}/Ansjer/file/wechatpay/apiclient_key.pem"
+        r = requests.post(url=self.refund_url, headers={'Content-Type': 'text/xml'}, data=xml.encode('utf-8'),
+                          cert=(cert, key), verify=True)
+        response = xmltodict.parse(r.text)
+        if response['xml']['return_code'] == 'SUCCESS':
+            return True
+        else:
+            return False