Django小程序微信支付与微信退款

it2024-04-12  47

Django小程序微信支付与微信退款

1.微信支付

1.1前期准备

需要准备微信支付的配置参数

# 微信支付的配置参数 client_appid = '' # 小程序appid client_secret = '' # 小程序secret Mch_id = '' # 商户号 Mch_key = '' # 商户Key order_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 订单地址

1.2用户下单

#下单接口 from django.db import transaction @transaction.atomic 添加事务 def place_order(request): if request.method == 'POST': #接收相关订单参数包括地址数量等等,因为前端需要传递数组,选择使用json格式接收,formdata自行改为request.POST.get r = json.loads(request.body.decode()) user = int(r['user_id']) good_id = r['good_id'] # 从数据库中获取商品单价和运费,这里的单位还是元 good_obj = models.Goods.objects.get(gid=good_id) unit_price = float(good_obj.now_price) unit_freight = float(good_obj.freight) money = unit_price*int(num) + unit_freight #获得用户id openid = getOpenid(user) # 获取客户端ip,因为这里使用nginx代理,所以这里写服务器ip即可 client_ip = 'xxx.xxx.xxx.xx' print(request.get_host()) # 请求微信的统一下单url,在上面配置参数有 url = order_url # 拿到封装好的xml数据 body_data,out_trade_no= get_bodyData(openid, client_ip, money, user) # 获取时间戳 timeStamp = str(int(time.time())) # 请求微信接口下单 import requests respone = requests.post(url, body_data.encode("utf-8"), headers={'Content-Type': 'application/xml'}) # 回复数据为xml,将其转为字典 content = xml_to_dict(respone.content) ret = {"state": 1000} sid = transaction.savepoint() #设置断点 if content["return_code"] == 'SUCCESS': # 获取预支付交易会话标识 prepay_id = content.get("prepay_id") package = 'prepay_id='+prepay_id # 获取随机字符串 nonceStr = content.get("nonce_str") # 获取paySign签名,这个需要我们根据拿到的prepay_id和nonceStr进行计算签名 paySign = get_paysign(prepay_id, timeStamp, nonceStr) # 封装返回给前端的数据 res_dict = {'rcode': 200, 'package': package, 'paysign': paySign, 'timestamp': timeStamp, 'nonceStr': nonceStr, 'appId': client_appid, 'orderNumber': out_trade_no} print('=========', res_dict) try: #以下是数据库存订单数据,根据自己需求存 order = models.Goods_Order.objects.create( good_id=good_id, user_id=user, num=num, unit_price = unit_price, unit_freight=unit_freight, money=money, order_number=out_trade_no, ., ., ., ., ) order_obj = models.Goods_Order.objects.get(order_number=out_trade_no) for v in avid: models.Order2Attr.objects.create( order_id=order.goid, attr_value_id=int(v) ) av_obj = models.Attr_Value.objects.get(avid=int(v)) av_obj.stocks = F('stocks')-int(num) av_obj.save() order_detail = models.Order_Detail.objects.update_or_create( order_id=order_obj.goid, payment=0, receipt=-1, aftersale=0 ) good = models.Goods.objects.get(gid=good_id) good.stocks = F('stocks')-int(num) good.save() return JsonResponse(res_dict,safe=False) except Exception as e: print(e) transaction.savepoint_rollback(sid) #数据库出错立即回滚! return JsonResponse({'msg':'下单失败'},safe=False) transaction.clean_savepoints() #清除保存点 else: ret["rcode"] = 500 ret["msg"] = "下单失败" return JsonResponse(ret,safe=False) #上面用到的封装好的相关函数 #获取用户openID def getOpenid(userid): userData = models.User.objects.get(uid=int(userid)) return userData.openID #随机字符串生成算法 def random_str(randomlength=32): str = '' chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' length = len(chars) - 1 random = Random() #生成0到1的随机浮点数 for i in range(randomlength): str+=chars[random.randint(0, length)] return str # 生成签名的函数 def paysign(appid, body, mch_id, nonce_str, notify_url, openid, out_trade_no, spbill_create_ip, total_fee): ret = { "appid": appid, "body": body, "mch_id": mch_id, "nonce_str": nonce_str, "notify_url": notify_url, "openid": openid, "out_trade_no": out_trade_no, "spbill_create_ip": spbill_create_ip, "total_fee": total_fee, "trade_type": 'JSAPI' } print(ret) # 处理函数,对参数按照key=value的格式,并按照参数名ASCII字典序排序 stringA = '&'.join(["{0}={1}".format(k, ret.get(k)) for k in sorted(ret)]) stringSignTemp = '{0}&key={1}'.format(stringA, Mch_key) sign = hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest() print(sign.upper()) return sign.upper() def xml_to_dict(xml_data): ''' xml to dict :param xml_data: :return: ''' xml_dict = {} root = ET.fromstring(xml_data) for child in root: xml_dict[child.tag] = child.text return xml_dict # 生成商品订单号 def getWxPayOrdrID(user): date = datetime.datetime.now() # 根据当前系统时间来生成商品订单号。时间精确到微秒 payOrdrID = date.strftime("%Y%m%d%H%M%S%f") # 加一个用户id保证订单号唯一 payOrdrID = str(user)+payOrdrID return payOrdrID # 获取返回给小程序的paySign def get_paysign(prepay_id, timeStamp, nonceStr): pay_data = { 'appId': client_appid, 'nonceStr': nonceStr, 'package': "prepay_id=" + prepay_id, 'signType': 'MD5', 'timeStamp': timeStamp } stringA = '&'.join(["{0}={1}".format(k, pay_data.get(k)) for k in sorted(pay_data)]) #按照字母顺序依次返回pay_data的值并用&连接 stringSignTemp = '{0}&key={1}'.format(stringA, Mch_key) sign = hashlib.md5(stringSignTemp.encode("utf-8")).hexdigest() return sign.upper() # 获取全部参数信息,封装成xml def get_bodyData(openid, client_ip, price, user): body = 'Mytest' # 商品描述 notify_url = 'http://www.xxx.com/goodorderNotify/' # 支付成功的回调地址 可访问 不带参数 nonce_str = random_str() # 随机字符串 out_trade_no = getWxPayOrdrID(user) # 商户订单号 total_fee = str(int(float(price)*100)) # 订单价格 单位是 分 # 获取签名 sign = paysign(client_appid, body, Mch_id, nonce_str, notify_url, openid, out_trade_no, client_ip, total_fee) bodyData = '<xml>' bodyData += '<appid>' + client_appid + '</appid>' # 小程序ID bodyData += '<body>' + body + '</body>' # 商品描述 bodyData += '<mch_id>' + Mch_id + '</mch_id>' # 商户号 bodyData += '<nonce_str>' + nonce_str + '</nonce_str>' # 随机字符串 bodyData += '<notify_url>' + notify_url + '</notify_url>' # 支付成功的回调地址 bodyData += '<openid>' + openid + '</openid>' # 用户标识 bodyData += '<out_trade_no>' + out_trade_no + '</out_trade_no>' # 商户订单号 bodyData += '<spbill_create_ip>' + client_ip + '</spbill_create_ip>' # 客户端终端IP bodyData += '<total_fee>' + total_fee + '</total_fee>' # 总金额 单位为分 bodyData += '<trade_type>JSAPI</trade_type>' # 交易类型 小程序取值如下:JSAPI bodyData += '<sign>' + sign + '</sign>' bodyData += '</xml>' return bodyData, out_trade_no

2.微信退款

1.1前期准备

微信支付平台–》账户中心 – 》API安全 – 》 API证书,弄到手以后解压存放在后端

我放在了和app同级的目录里

1.2用户退款

#退款api def refund_api(request): if request.method == 'POST': print(111) order_id = int(request.POST.get('order_id','')) refund_fee = str(request.POST.get('refund_fee','')) result = dict() # try: # 获得退款订单号和订单号 order_obj = models.Goods_Order.objects.get(goid=order_id) out_refund_no = getWxPayOrdrID(order_obj.user_id) trade_no = order_obj.order_number # 存入提交退款时间和单号 now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') After_obj = models.AfterSale.objects.filter(order_id=order_id).update( retund_time=now, refund_number=out_refund_no, ) # 将订单金额转换为分 total_fee = int(float(order_obj.money)*100) refund_fee = int(float(refund_fee)*100) res = refund(trade_no, out_refund_no, total_fee, refund_fee, order_id) if res['flag'] == True: result['flag'] = True else: pass # except Exception as e: # print(e) # result['flag'] = False return JsonResponse(result,safe=False) def refund(trade_no, out_refund_no, total_fee, refund_fee,order_id): ''' :param trade_no: 创建订单时自动生成的订单号 :param out_refund_no: 商户退款单号 :param total_fee: 订单金额 :param refund_fee: 退款金额 :return: ''' info = { 'appid':client_appid, 'mch_id':Mch_id, 'out_trade_no':trade_no, 'nonce_str':random_str(), 'sign_type':'MD5', 'out_refund_no':out_refund_no, 'total_fee':total_fee, 'refund_fee':refund_fee } key = Mch_key string = "&".join([f"{k}={info[k]}" for k in sorted(info)] + [f"{'key'}={key}"]) info['sign'] = md5(string).upper() xml = "<xml>{}</xml>".format("".join([f"<{k}>{v}</{k}>" for k,v in info.items()])) cert = f"{settings.BASE_DIR}/cert/apiclient_cert.pem" key = f"{settings.BASE_DIR}/cert/apiclient_key.pem" import requests res = requests.post( url = 'https://api.mch.weixin.qq.com/secapi/pay/refund', data=xml.encode('utf-8'), headers={ 'Accept-Language':'zh-CN,zh;q=0.9' }, cert=(cert,key), verify=True ) r = res.text.encode('ISO-8859-1').decode('utf-8') response = xmltodict.parse(r) result = dict() print(response) if response['xml']['return_code'] == 'SUCCESS': #退款成功,修改状态,添加退款到钱包的时间 detail_obj = models.Order_Detail.objects.filter(order_id=order_id).update(aftersale=2) now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') after_obj = models.AfterSale.objects.filter(order_id=order_id).update( received_time=now, refund_result=3, refund_fee=refund_fee ) result['flag'] = True else: result['flag'] = False return result
最新回复(0)