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

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
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值