wechatpy.pay.api.withhold 源代码

# -*- coding: utf-8 -*-

import time
import random
from datetime import datetime

from optionaldict import optionaldict

from wechatpy.utils import timezone
from wechatpy.pay.utils import get_external_ip, calculate_signature
from wechatpy.pay.api.base import BaseWeChatPayAPI


[文档]class WeChatWithhold(BaseWeChatPayAPI):
[文档] def apply_signing( self, plan_id, contract_code, contract_display_account, notify_url, version="1.0", clientip=None, deviceid=None, mobile=None, email=None, qq=None, request_serial=None, openid=None, creid=None, outerid=None, ): """ 申请签约 api https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_1&index=1 :param plan_id: 模板id 协议模板id,设置路径见开发步骤。 :param contract_code: 签约协议号 商户侧的签约协议号,由商户生成 :param contract_display_account: 用户账户展示名称 签约用户的名称,用于页面展示,页面样例可见案例与规范 :param notify_url: 回调通知url 用于接收签约成功消息的回调通知地址,以http或https开头。 :param version: 版本号 固定值1.0 :param request_serial: 可选 请求序列号 商户请求签约时的序列号,商户侧须唯一。序列号主要用于排序,不作为查询条件 :param clientip: 可选 客户端 IP 点分IP格式(客户端IP) :param deviceid: 可选 设备ID android填imei的一次md5; ios填idfa的一次md5 :param mobile: 可选 手机号 用户手机号 :param email: 可选 邮箱地址 用户邮箱地址 :param qq: 可选 QQ号 用户QQ号 :param openid: 可选 微信open ID 用户微信open ID :param creid: 可选 身份证号 用户身份证号 :param outerid: 可选 商户侧用户标识 用户在商户侧的标识 :return: 返回的结果数据字典 """ timestamp = int(time.time()) if request_serial is None: request_serial = int(time.time() * 1000) data = { "appid": self.appid, "mch_id": self.mch_id, "sub_mch_id": self.sub_mch_id, "plan_id": plan_id, "contract_code": contract_code, "request_serial": request_serial, "contract_display_account": contract_display_account, "notify_url": notify_url, "version": version, "timestamp": timestamp, "clientip": clientip, "deviceid": deviceid, "mobile": mobile, "email": email, "qq": qq, "openid": openid, "creid": creid, "outerid": outerid, } data = optionaldict(data) sign = calculate_signature(data, self._client.api_key) data["sign"] = sign return { "base_url": f"{self._client.API_BASE_URL}papay/entrustweb", "data": data, }
[文档] def query_signing( self, contract_id=None, plan_id=None, contract_code=None, openid=None, version="1.0", ): """ 查询签约关系 api :param contract_id: 可选 委托代扣协议id 委托代扣签约成功后由微信返回的委托代扣协议id,选择contract_id查询,则此参数必填 :param plan_id: 可选 模板id 商户在微信商户平台配置的代扣模板id,选择plan_id+contract_code查询,则此参数必填 :param contract_code: 可选 签约协议号 商户请求签约时传入的签约协议号,商户侧须唯一。选择plan_id+contract_code查询,则此参数必填 :param openid: 可选 openid 用户标识,必须保证与传入appid对应 :param version: 版本号 固定值1.0 :return: 返回的结果信息 """ if not contract_id and not (plan_id and contract_code) and not (plan_id and openid): raise ValueError("contract_id and (plan_id, contract_code) and (plan_id, openid) must be a choice.") data = { "appid": self.appid, "mch_id": self.mch_id, "contract_id": contract_id, "plan_id": plan_id, "contract_code": contract_code, "openid": openid, "version": version, "nonce_str": None, } return self._post("papay/querycontract", data=data)
[文档] def apply_deduct( self, body, total_fee, contract_id, notify_url, out_trade_no=None, detail=None, attach=None, fee_type="CNY", goods_tag=None, clientip=None, deviceid=None, mobile=None, email=None, qq=None, openid=None, creid=None, outerid=None, ): """ 申请扣款 api :param body: 商品描述 商品或支付单简要描述 :param out_trade_no: 可选 商户订单号 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号 :param total_fee: 总金额 订单总金额,单位为分,只能为整数,详见支付金额 :param contract_id: 委托代扣协议id 签约成功后,微信返回的委托代扣协议id :param notify_url: 回调通知url 接受扣款结果异步回调通知的url :param detail: 可选 商品详情 商品名称明细列表 :param attach: 可选 附加数据 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 :param fee_type: 可选 货币类型 符合ISO 4217标准的三位字母代码,默认人民币:CNY :param goods_tag: 可选 商品标记 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠 :param clientip: 可选 客户端 IP 点分IP格式(客户端IP) :param deviceid: 可选 设备ID android填imei的一次md5; ios填idfa的一次md5 :param mobile: 可选 手机号 用户手机号 :param email: 可选 邮箱地址 用户邮箱地址 :param qq: 可选 QQ号 用户QQ号 :param openid: 可选 微信open ID 用户微信open ID :param creid: 可选 身份证号 用户身份证号 :param outerid: 可选 商户侧用户标识 用户在商户侧的标识 :return: 返回的结果信息 """ trade_type = "PAP" # 交易类型 交易类型PAP-微信委托代扣支付 timestamp = int(time.time()) # 10位时间戳 spbill_create_ip = get_external_ip() # 终端IP 调用微信支付API的机器IP if not out_trade_no: now = datetime.fromtimestamp(time.time(), tz=timezone("Asia/Shanghai")) out_trade_no = f"{self.mch_id}{now.strftime('%Y%m%d%H%M%S')}{random.randint(1000, 10000)}" data = { "appid": self.appid, "mch_id": self.mch_id, "body": body, "out_trade_no": out_trade_no, "total_fee": total_fee, "trade_type": trade_type, "contract_id": contract_id, "notify_url": notify_url, "detail": detail, "attach": attach, "fee_type": fee_type, "goods_tag": goods_tag, "clientip": clientip, "deviceid": deviceid, "mobile": mobile, "email": email, "qq": qq, "openid": openid, "creid": creid, "outerid": outerid, "timestamp": timestamp, "spbill_create_ip": spbill_create_ip, } return self._post("pay/pappayapply", data=data)
[文档] def query_order(self, transaction_id=None, out_trade_no=None): """ 查询订单 api :param transaction_id: 二选一 微信订单号 微信的订单号,优先使用 :param out_trade_no: 二选一 商户订单号 商户系统内部的订单号,当没提供transaction_id时需要传这个。 :return: 返回的结果信息 """ if not transaction_id and not out_trade_no: raise ValueError("transaction_id and out_trade_no must be a choice.") data = { "appid": self.appid, "mch_id": self.mch_id, "transaction_id": transaction_id, "out_trade_no": out_trade_no, } return self._post("pay/paporderquery", data=data)
[文档] def apply_cancel_signing( self, contract_id=None, plan_id=None, contract_code=None, contract_termination_remark=None, version="1.0", ): """ 申请解约 https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_4&index=6 :param contract_id: 合同ID :param plan_id: 模板ID :param contract_code: 合同号 :param contract_termination_remark: 解约原因 :param version: 版本号 :return: """ if not (contract_id or (plan_id and contract_code)): raise ValueError("contract_id and (plan_id, contract_code) must be a choice.") data = { "appid": self.appid, "mch_id": self.mch_id, "plan_id": plan_id, "contract_code": contract_code, "contract_id": contract_id, "contract_termination_remark": contract_termination_remark, "version": version, "nonce_str": None, } return self._post("papay/deletecontract", data=data)