评论

三行代码搞定V3版微信支付接口

一直未等到微信支付v3版官方python sdk,只好从头造了个轮子。

wechatpayv3


介绍

微信支付接口V3版python库。

欢迎微信支付开发者扫码进QQ群讨论:

QQ群号:973102221

适用对象

wechatpayv3支持微信支付直连商户,接口说明详见 官网

特性

  1. 平台证书自动更新,无需开发者关注平台证书有效性,无需手动下载更新;
  2. 支持本地缓存平台证书,初始化时指定平台证书保存目录即可;
  3. 敏感信息直接传入明文参数,SDK内部自动加密,无需手动处理。

适配进度

微信支付V3版API接口

其中:

基础支付

JSAPI支付 已适配
APP支付 已适配
H5支付 已适配
Native支付 已适配
小程序支付 已适配
合单支付 已适配
付款码支付 待官网更新
刷脸支付 无需适配

行业方案

智慧商圈 已适配
微信支付分停车服务 已适配

营销工具

图片上传(营销专用) 已适配

经营能力

支付即服务 已适配

其他能力

图片上传 已适配
视频上传 已适配

需要的接口还没有适配怎么办?

由于wechatpayv3包内核心的core.py已经封装了请求签名和消息验证过程,开发者无需关心web请求细节,直接根据官方文档参考以下基础支付的申请退款接口代码自行适配,测试OK的话,欢迎提交代码。
必填的参数建议加上空值检查,可选的参数默认传入None。参数类型对照参考下表:

文档声明 wechatpayv3
string string
int int
object dict: {}
array list: []
boolean bool: True, False
def refund(self,
           out_refund_no,
           amount,
           transaction_id=None,
           out_trade_no=None,
           reason=None,
           funds_account=None,
           goods_detail=None,
           notify_url=None):
    """申请退款
    :param out_refund_no: 商户退款单号,示例值:'1217752501201407033233368018'
    :param amount: 金额信息,示例值:{'refund':888, 'total':888, 'currency':'CNY'}
    :param transaction_id: 微信支付订单号,示例值:'1217752501201407033233368018'
    :param out_trade_no: 商户订单号,示例值:'1217752501201407033233368018'
    :param reason: 退款原因,示例值:'商品已售完'
    :param funds_account: 退款资金来源,示例值:'AVAILABLE'
    :param goods_detail: 退款商品,示例值:{'merchant_goods_id':'1217752501201407033233368018', 'wechatpay_goods_id':'1001', 'goods_name':'iPhone6s 16G', 'unit_price':528800, 'refund_amount':528800, 'refund_quantity':1}
    :param notify_url: 通知地址,示例值:'https://www.weixin.qq.com/wxpay/pay.php'
    """
    params = {}
    params['notify_url'] = notify_url or self._notify_url
    # 
    if out_refund_no:
        params.update({'out_refund_no': out_refund_no})
    else:
        raise Exception('out_refund_no is not assigned.')
    if amount:
        params.update({'amount': amount})
    else:
        raise Exception('amount is not assigned.')
    if transaction_id:
        params.update({'transaction_id': transaction_id})
    if out_trade_no:
        params.update({'out_trade_no': out_trade_no})
    if reason:
        params.update({'reason': reason})
    if funds_account:
        params.update({'funds_account': funds_account})
    if goods_detail:
        params.update({'goods_detail': goods_detail})
    path = '/v3/refund/domestic/refunds'
    return self._core.request(path, method=RequestType.POST, data=params)

源码

github

gitee

安装

$ pip install wechatpayv3

使用方法

准备

参考微信官方文档准备好密钥, 证书文件和配置(证书/密钥/签名介绍)

初始化

from wechatpayv3 import WeChatPay, WeChatPayType

# 微信支付商户号
MCHID = '1230000109'
# 商户证书私钥
PRIVATE_KEY = open('path_to_key/apiclient_key.pem').read()
# 商户证书序列号
CERT_SERIAL_NO = '444F4864EA9B34415...'
# API v3密钥, https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml
APIV3_KEY = 'MIIEvwIBADANBgkqhkiG9w0BAQE...'
# APPID
APPID = 'wxd678efh567hg6787'
# 回调地址,也可以在调用接口的时候覆盖
NOTIFY_URL = 'https://www.weixin.qq.com/wxpay/pay.php'
# 微信支付平台证书缓存目录
CERT_DIR = './cert'

wxpay = WeChatPay(
    wechatpay_type=WeChatPayType.MINIPROG,
    mchid=MCHID,
    private_key=PRIVATE_KEY,
    cert_serial_no=CERT_SERIAL_NO,
    apiv3_key=APIV3_KEY,
    appid=APPID,
    notify_url=NOTIFY_URL,
    cert_dir=CERT_DIR)

接口


# 统一下单
def pay():
    code, message = wxpay.pay(
        description='demo-description',
        out_trade_no='demo-trade-no',
        amount={'total': 100},
        payer={'openid': 'demo-openid'}
    )
    print('code: %s, message: %s' % (code, message))

# 订单查询
def query():
    code, message = wxpay.query(
        transaction_id='demo-transation-id'
    )
    print('code: %s, message: %s' % (code, message))

# 关闭订单
def close():
    code, message = wxpay.close(
        out_trade_no='demo-out-trade-no'
    )
    print('code: %s, message: %s' % (code, message))

# 申请退款
def refund():
    code, message=wxpay.refund(
        out_refund_no='demo-out-refund-no',
        amount={'refund': 100, 'total': 100, 'currency': 'CNY'},
        transaction_id='1217752501201407033233368018'
    )
    print('code: %s, message: %s' % (code, message))

# 退款查询
def query_refund():
    code, message = wxpay.query_refund(
        out_refund_no='demo-out-refund-no'
    )
    print('code: %s, message: %s' % (code, message))

# 申请交易账单
def trade_bill():
    code, message = wxpay.trade_bill(
        bill_date='2021-04-01'
    )
    print('code: %s, message: %s' % (code, message))

# 申请资金流水账单
def fundflow_bill():
    code, message = wxpay.fundflow_bill(
        bill_date='2021-04-01'
    )
    print('code: %s, message: %s' % (code, message))

# 下载账单
def download_bill():
    code, message = wxpay.download_bill(
        url='https://api.mch.weixin.qq.com/v3/billdownload/file?token=demo-token'
    )
    print('code: %s, message: %s' % (code, message))

# 合单支付下单
def combine_pay():
    code, message = wxpay.combine_pay(
        combine_out_trade_no='demo_out_trade_no',
        sub_orders=[{'mchid':'1900000109',
                     'attach':'深圳分店',
                     'amount':{'total_amount':100,'currency':'CNY'},
                     'out_trade_no':'20150806125346',
                     'description':'腾讯充值中心-QQ会员充值',
                     'settle_info':{'profit_sharing':False, 'subsidy_amount':10}}]
    )
    print('code: %s, message: %s' % (code, message))

# 合单订单查询
def combine_query():
    code, message = wxpay.combine_query(
        combine_out_trade_no='demo_out_trade_no'
    )
    print('code: %s, message: %s' % (code, message))

# 合单订单关闭
def combine_close():
    code, message = wxpay.combine_close(
        combine_out_trade_no='demo_out_trade_no', 
        sub_orders=[{'mchid': '1900000109', 'out_trade_no': '20150806125346'}]
    )
    print('code: %s, message: %s' % (code, message))

# 计算签名供调起支付时拼凑参数使用
# 注意事项:注意参数顺序,某个参数为空时不能省略,以空字符串''占位
def sign():
    print(wxpay.sign(['wx888','1414561699','5K8264ILTKCH16CQ2502S....','prepay_id=wx201410272009395522657....']))

# 验证并解密回调消息,把回调接口收到的headers和body传入
def decrypt_callback(headers, body):
    print(wxpay.decrypt_callback(headers, body))

# 智慧商圈积分通知
def points_notify():
    code, message = wxpay.points_notify(
        transaction_id='4200000533202000000000000000',
        openid='otPAN5xxxxxxxxrOEG6lUv_pzacc',
        earn_points=True,
        increased_points=100,
        points_update_time='2020-05-20T13:29:35.120+08:00'
    )
    print('code: %s, message: %s' % (code, message))

# 智慧商圈积分授权查询
def user_authorization():
    code, message = wxpay.user_authorizations(
        openid='otPAN5xxxxxxxxrOEG6lUv_pzacc'
    )
    print('code: %s, message: %s' % (code, message))

# 支付即服务人员注册
def guides_register():
    code, message = wxpay.guides_register(
        corpid='1234567890',
        store_id=1234,
        userid='rebert',
        name='robert',
        mobile='13900000000',
        qr_code='https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxx',
        avatar='http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0',
        group_qrcode='http://p.qpic.cn/wwhead/nMl9ssowtibVGyrmvBiaibzDtp/0'
    )
    print('code: %s, message: %s' % (code, message))

# 支付即服务人员分配
def guides_assign():
    code, message = wxpay.guides_assign(
        guide_id='LLA3WJ6DSZUfiaZDS79FH5Wm5m4X69TBic',
        out_trade_no='20150806125346'
    )
    print('code: %s, message: %s' % (code, message))

# 支付即服务人员查询
def guides_query():
    code, message = wxpay.guides_query(
        store_id=1234,
        userid='robert',
        mobile='13900000000',
        work_id='robert',
        limit=5,
        offset=0
    )
    print('code: %s, message: %s' % (code, message))

# 支付即服务人员信息更新
def guides_update():
    code, message = wxpay.guides_update(
        guide_id='LLA3WJ6DSZUfiaZDS79FH5Wm5m4X69TBic',
        name='robert',
        mobile='13900000000',
        qr_code='https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxx',
        avatar='http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0',
        group_qrcode='http://p.qpic.cn/wwhead/nMl9ssowtibVGyrmvBiaibzDtp/0'
    )
    print('code: %s, message: %s' % (code, message))

# 图片上传
def image_upload():
    code, message = wxpay.image_upload(
        filepath='./media/demo.png'
    )
    print('code: %s, message: %s' % (code, message))

# 视频上传
def video_upload():
    code, message = wxpay.video_upload(
        filepath='./media/demo.mp4'
    )
    print('code: %s, message: %s' % (code, message))

# 查询车牌服务开通信息
def parking_service_find():
    code, message = wxpay.parking_service_find(
        plate_number='粤B888888',
        plate_color='BLUE',
        openid='oUpF8uMuAJOM2pxb1Q'
    )
    print('code: %s, message: %s' % (code, message))

# 创建停车入场
def parking_enter():
    code, message = wxpay.parking_enter(
        out_parking_no='1231243',
        plate_number='粤B888888',
        plate_color='BLUE',
        notify_url='https://yoursite.com/wxpay.html',
        start_time='2017-08-26T10:43:39+08:00',
        parking_name='欢乐海岸停车场',
        free_duration=3600
    )
    print('code: %s, message: %s' % (code, message))

# 停车扣费受理
def parking_order():
    code, message = wxpay.parking_order(
        description='停车场扣费',
        out_trade_no='20150806125346',
        notify_url='https://yoursite.com/wxpay.html',
        total=888,
        parking_id='5K8264ILTKCH16CQ250',
        plate_number='粤B888888',
        plate_color='BLUE',
        start_time='2017-08-26T10:43:39+08:00',
        end_time='2017-08-26T10:43:39+08:00',
        parking_name='欢乐海岸停车场',
        charging_duration=3600,
        device_id='12313'
    )
    print('code: %s, message: %s' % (code, message))

# 查询停车扣费订单
def parking_order_query():
    code, message = wxpay.parking_order_query(
        out_trade_no='20150806125346'
    )
    print('code: %s, message: %s' % (code, message))

# 图片上传(营销专用)
def marking_image_upload():
    code, message = wxpay.marketing_image_upload(
        filepath='./media/demo.png'
    )
    print('code: %s, message: %s' % (code, message))
最后一次编辑于  08-14  
点赞 2
收藏
评论

8 个评论

  • 北望沣渭
    北望沣渭
    04-14

    👍 建议打个标签「微信支付文章活动月」

    04-14
    赞同 2
    回复
  • 支付社区运营-Tasting
    支付社区运营-Tasting
    04-14

    点击了解活动详情

    04-14
    赞同 1
    回复
  • 老牛
    老牛
    09-18
    v
    09-18
    赞同
    回复
  • 可利马擦
    可利马擦
    08-13

    执行统一下单接口 pay() 报错:

    Traceback (most recent call last):

        wxpay = WeChatPay(

    TypeError: __init__() got an unexpected keyword argument 'parivate_key'

    08-13
    赞同
    回复 4
    • 可利马擦
      可利马擦
      08-13
      parivate_key=PRIVATE_KEY 这里写错了
      08-13
      回复
    • 可利马擦
      可利马擦
      08-13
      这个报错能不能帮看下?
      ValueError: Could not deserialize key data. The data may be in an incorrect format or it may be encrypted with an unsupported algorithm.
      08-13
      回复
    • 可利马擦
      可利马擦
      08-14回复可利马擦
      已解决,私钥文件格式不对
      08-14
      回复
    • 陈刚
      陈刚
      08-14
      拼写错误已修复,私玥导入不直观的问题已在readme中更新。
      08-14
      回复
  • 朱子玉
    朱子玉
    07-26

    下载账单默认返回的是gzip,但是request返回的是response.text。老师能不能更新下给下载账单单独写个request,直接返回response就好,当然解个压更好了。

    07-26
    赞同
    回复 1
    • 陈刚
      陈刚
      08-14
      准备在后面更新中实现
      08-14
      回复
  • 花初开工作室
    花初开工作室
    07-11

    支付的时候,签名方法,报错:ValueError: Could not deserialize key data.

    知道为啥吗

    07-11
    赞同
    回复 3
    • 陈刚
      陈刚
      07-29
      请检查你的私钥文件格式。
      07-29
      回复
    • Sharp
      Sharp
      08-12回复陈刚
      我也遇到这个错误,就不能把这个问题重点写在文档中
      08-12
      回复
    • 陈刚
      陈刚
      08-14回复Sharp
      已经在readme中更新了
      08-14
      回复
  • 风吹叶落^_^
    风吹叶落^_^
    06-16

    这些参数好像没有对应上,不是同一值还是失误注释写错了?

    06-16
    赞同
    回复 1
    • 陈刚
      陈刚
      07-29
      请更新到最新版,参数做了微调,减少了不必要的参数。
      07-29
      回复
  • I 夏 荷兰
    I 夏 荷兰
    04-17

    签名验证有问题

    04-17
    赞同
    回复 5
    • I 夏 荷兰
      I 夏 荷兰
      04-17
      github 是正常的,pip 有点问题
      04-17
      回复
    • 陈刚
      陈刚
      04-19回复I 夏 荷兰
      pip更新比github稍微迟点
      04-19
      回复
    • G&Y&M
      G&Y&M
      07-28
      您好,请问 签名验证解决了么?
      07-28
      回复
    • G&Y&M
      G&Y&M
      07-28
      不走verify这个方法,直接decrypt解析数据能得到结果
      07-28
      回复
    • 陈刚
      陈刚
      07-29
      最新版已经内置签名验证。
      07-29
      回复
登录 后发表内容