# -*- coding: utf-8 -*- import os from .media import _media_upload from .type import RequestType from .utils import sha256 def marketing_image_upload(self, filepath, filename=None): """图片上传(营销专用) :param filepath: 图片文件路径 :param filename: 文件名称,未指定则从filepath参数中截取 """ return _media_upload(self, filepath, filename, '/v3/marketing/favor/media/image-upload') def marketing_card_send(self, card_id, openid, out_request_no, send_time, appid=None): """发放消费卡 :card_id: 消费卡ID。示例值:'pIJMr5MMiIkO_93VtPyIiEk2DZ4w' :openid: 用户openid,待发卡用户的openid。示例值:'obLatjhnqgy2syxrXVM3MJirbkdI' :out_request_no: 商户单据号。示例值:'oTYhjfdsahnssddj_0136' :send_time: 请求发卡时间,单次请求发卡时间,消费卡在商户系统的实际发放时间,为东八区标准时间(UTC+8)。示例值:'2019-12-31T13:29:35.120+08:00' :param appid: 应用ID,可不填,默认传入初始化时的appid,示例值:'wx1234567890abcdef' """ params = {} if card_id: path = '/v3/marketing/busifavor/coupons/%s/send' % card_id else: raise Exception('card_id is not assigned.') if openid: params.update({'openid': openid}) else: raise Exception('openid is not assigned.') if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') if send_time: params.update({'send_time': send_time}) else: raise Exception('send_time is not assigned.') params.update({'appid': appid or self._appid}) return self._core.request(path, method=RequestType.POST, data=params) def marketing_partnership_build(self, idempotency_key, partner_type, business_type, partner_appid=None, partner_merchant_id=None, stock_id=None): """建立合作关系 :idempotency_key: 业务请求幂等值,商户侧需保持唯一性,可包含英文字母,数字,|,_,*,-等内容,不允许出现其他不合法符号。示例值:'12345' :partner_type: 合作方类别,枚举值:'APPID':合作方为APPID,'MERCHANT':合作方为商户。示例值:'APPID' :business_type: 授权业务类别,枚举值:'FAVOR_STOCK':代金券批次,'BUSIFAVOR_STOCK':商家券批次。示例值:'FAVOR_STOCK' :partner_appid: 合作方APPID,合作方类别为APPID时必填。示例值:'wx4e1916a585d1f4e9' :partner_merchant_id: 合作方商户ID,合作方类别为MERCHANT时必填。特殊规则:最小字符长度为8。示例值:'2480029552' :stock_id: 授权批次ID,授权业务类别为商家券批次或代金券批次时,此参数必填。示例值:'2433405' """ headers = {} if idempotency_key: headers.update({'Idempotency-Key': idempotency_key}) else: raise Exception('idempotency_key is not assigned.') params = {} if partner_type == 'APPID' and partner_appid: params.update({'partner': {'type': partner_type, 'appid': partner_appid}}) elif partner_type == 'MERCHANT' and partner_merchant_id: params.update({'partner': {'type': partner_type, 'merchant_id': partner_merchant_id}}) else: raise Exception('invalid value in partner_type/partner_appid/partner_merchant_id') if business_type not in ['FAVOR_STOCK', 'BUSIFAVOR_STOCK'] or not stock_id: raise Exception('invalid value in bussiness_type/stock_id.') params.update({'authorized_data': {'bussiness_type': business_type, 'stock_id': stock_id}}) path = '/v3/marketing/partnerships/build' return self._core.request(path, method=RequestType.POST, data=params, headers=headers) def marketing_partnership_query(self, business_type, stock_id, partner_type=None, partner_appid=None, partner_merchant_id=None, limit=20, offset=None): """查询合作关系列表 :business_type: 授权业务类别,枚举值:'FAVOR_STOCK':代金券批次,'BUSIFAVOR_STOCK':商家券批次。示例值:'FAVOR_STOCK' :stock_id: 授权批次ID,授权业务类别为商家券批次或代金券批次时,此参数必填。示例值:'2433405' :partner_type: 合作方类别,枚举值:'APPID':合作方为APPID,'MERCHANT':合作方为商户。示例值:'APPID' :partner_appid: 合作方APPID,合作方类别为APPID时必填。示例值:'wx4e1916a585d1f4e9' :partner_merchant_id: 合作方商户ID,合作方类别为MERCHANT时必填。特殊规则:最小字符长度为8。示例值:'2480029552' :limit: 分页大小,最大50。不传默认为20。示例值:5 :offset: 分页页码,页码从0开始。示例值:10 """ path = '/v3/marketing/partnerships?' if business_type not in ['FAVOR_STOCK', 'BUSIFAVOR_STOCK'] or not stock_id: raise Exception('invalid value in bussiness_type/stock_id.') path = '%sauthorized_data={"business_type":"%s","stock_id":"%s"}' % (path, business_type, stock_id) if partner_type == 'APPID' and partner_appid: path = '%s&partner={"type":"%s","appid":"%s"}' % (path, partner_type, partner_appid) elif partner_type == 'MERCHANT' and partner_merchant_id: path = '%s&partner={"type":"%s","merchant_id":"%s"}' % (path, partner_type, partner_merchant_id) if limit in range(0, 51): path = '%s&limit=%s' % (path, limit) if offset: path = '%s&offset=%s' % (path, offset) return self._core.request(path) def marketing_paygift_activity_create(self, activity_base_info, award_send_rule, advanced_setting=None): """创建全场满额送活动 :param activity_base_info: 活动基本信息 :param award_send_rule: 活动奖品发放规则 :param advanced_setting: 活动高级设置 """ params = {} if not activity_base_info or not award_send_rule: raise Exception('activity_base_info or award_send_rule is not assigned.') params.update({'activity_base_info': activity_base_info}) params.update({'award_send_rule': award_send_rule}) if advanced_setting: params.update({'advanced_setting': advanced_setting}) path = '/v3/marketing/paygiftactivity/unique-threshold-activity' return self._core.request(path, method=RequestType.POST, data=params) def marketing_paygift_activity_detail(self, activity_id): """查询活动详情接口 :param activity_id: 活动id,示例值:'10028001' """ if activity_id: path = '/v3/marketing/paygiftactivity/activities/%s' % activity_id else: raise Exception('activity_id is not assigned.') return self._core.request(path) def marketing_paygift_merchants_list(self, activity_id, offset=0, limit=20): """查询活动发券商户号 :param activity_id: 活动id,示例值:'10028001' :param offset:分页页码,页面从0开始。示例值:1 :param limit: 分页大小,限制分页最大数据条目。示例值:20 """ if activity_id: path = '/v3/marketing/paygiftactivity/activities/%s/merchants' % activity_id else: raise Exception('activity_id is not assigned.') path = '%s?offset=%s&limit=%s' % (path, offset, limit) return self._core.request(path) def marketing_paygift_goods_list(self, activity_id, offset=0, limit=20): """查询活动指定商品列表 :param activity_id: 活动id,示例值:'10028001' :param offset:分页页码,页面从0开始。示例值:1 :param limit: 分页大小,限制分页最大数据条目。示例值:20 """ if activity_id: path = '/v3/marketing/paygiftactivity/activities/%s/goods' % activity_id else: raise Exception('activity_id is not assigned.') path = '%s?offset=%s&limit=%s' % (path, offset, limit) return self._core.request(path) def marketing_paygift_activity_terminate(self, activity_id): """终止活动 :param activity_id: 活动id,示例值:'10028001' """ if activity_id: path = '/v3/marketing/paygiftactivity/activities/%s/terminate' % activity_id else: raise Exception('activity_id is not assigned.') return self._core.request(path, method=RequestType.POST) def marketing_paygift_merchant_add(self, activity_id, add_request_no, merchant_id_list=[]): """新增活动发券商户号 :param activity_id: 活动id,示例值:'10028001' :param add_request_no: 请求业务单据号,商户添加发券商户号的凭据号,商户侧需保持唯一性。示例值:'100002322019090134234sfdf' :param merchant_id_list: 发券商户号,新增到活动中的发券商户号列表,特殊规则:最小字符长度为8,最大为15,条目个数限制:[1,500]。示例值:["10000022","10000023"] """ if activity_id: path = '/v3/marketing/paygiftactivity/activities/%s/merchants/add' % activity_id else: raise Exception('activity_id is not assigned.') params = {} if add_request_no: params.update({'add_request_no': add_request_no}) else: raise Exception('add_request_no is not assigned.') if merchant_id_list: params.update({'merchant_id_list': merchant_id_list}) return self._core.request(path, method=RequestType.POST, data=params) def marketing_paygift_activity_list(self, offset=0, limit=20, activity_name=None, activity_status=None, award_type=None): """获取支付有礼活动列表 :param offset:分页页码,页面从0开始。示例值:1 :param limit: 分页大小,限制分页最大数据条目。示例值:20 :param activity_name: 活动名称,支持模糊搜索。示例值:'良品铺子回馈活动' :param activity_status: 活动状态,枚举值:'ACT_STATUS_UNKNOWN':状态未知,'CREATE_ACT_STATUS':已创建,'ONGOING_ACT_STATUS':运行中,'TERMINATE_ACT_STATUS':已终止, 'STOP_ACT_STATUS':已暂停,'OVER_TIME_ACT_STATUS':已过期,'CREATE_ACT_FAILED':创建活动失败。示例值:'CREATE_ACT_STATUS' :param award_type: 奖品类型,暂时只支持商家券。'BUSIFAVOR':商家券。示例值:'BUSIFAVOR' """ params = {} params.update({'offset': offset}) params.update({'limit': limit}) if activity_name: params.update({'activity_name': activity_name}) if activity_status: params.update({'activity_status': activity_status}) if award_type: params.update({'award_type': award_type}) path = '/v3/marketing/paygiftactivity/activities' return self._core.request(path, method=RequestType.POST, data=params) def marketing_paygift_merchant_delete(self, activity_id, merchant_id_list=[], delete_request_no=None): """删除活动发券商户号 :param activity_id: 活动id,示例值:'10028001' :param delete_request_no: 请求业务单据号,商户创建批次凭据号(格式:商户id+日期+流水号),商户侧需保持唯一性,可包含英文字母,数字,|,_,*,-等内容,不允许出现其他不合法符号。示例值:'100002322019090134234sfdf' :param merchant_id_list: 删除的发券商户号,从活动已有的发券商户号中移除的商户号列表,特殊规则:最小字符长度为8,最大为15,条目个数限制:[1,500]。示例值:["10000022","10000023"] """ if activity_id: path = '/v3/marketing/paygiftactivity/activities/%s/merchants/delete' % activity_id else: raise Exception('activity_id is not assigned.') params = {} if merchant_id_list: params.update({'merchant_id_list': merchant_id_list}) if delete_request_no: params.update({'delete_request_no': delete_request_no}) return self._core.request(path, method=RequestType.POST, data=params) def marketing_favor_stock_create(self, stock_name, belong_merchant, available_begin_time, available_end_time, stock_use_rule, coupon_use_rule, out_request_no, stock_type='NORMAL', no_cash=False, comment=None, pattern_info=None, ext_info=None): """创建代金券批次 :param stock_name: 批次名称,示例值:'微信支付代金券批次' :param belong_merchant: 归属商户号。示例值:'98568865' :param available_begin_time: 可用时间-开始时间,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE。示例值:'2015-05-20T13:29:35.120+08:00' :param available_end_time: 可用时间-结束时间,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE。示例值:'2015-05-20T13:29:35.120+08:00' :param stock_use_rule: 发放规则。示例值:{'max_coupons':5, 'max_amount':100, 'max_coupons_per_user':1, 'natural_person_limit':False, 'prevent_api_abuse':True} :param coupon_use_rule: 核销规则。示例值:{'available_merchants':['9856000','9856111']} :param out_request_no: 商户单据号,可包含英文字母,数字,|,_,*,-等内容,不允许出现其他不合法符号,商户侧需保持商户单据号全局唯一。示例值:'89560002019101000121' :param stock_type: 批次类型,仅支持:'NORMAL':固定面额满减券批次。示例值:'NORMAL' :param no_cash: 营销经费,枚举值:True:免充值,False:预充值。示例值:False :param comment: 批次备注,仅制券商户可见,用于自定义信息。校验规则:批次备注最多60个UTF8字符数。示例值:'零售批次' :param pattern_info: 样式设置,示例值:{'description':'微信支付营销代金券'} :param ext_info: 扩展属性,json格式字符串,如无需要则不填写。示例值:"{'exinfo1':'1234','exinfo2':'3456'}" """ params = {} if stock_name: params.update({'stock_name': stock_name}) else: raise Exception('stock_name is not assigned.') if belong_merchant: params.update({'belong_merchant': belong_merchant}) else: raise Exception('belong_merchant is not assigned.') if available_begin_time: params.update({'available_begin_time': available_begin_time}) else: raise Exception('available_begin_time is not assigned.') if available_end_time: params.update({'available_end_time': available_end_time}) else: raise Exception('available_end_time is not assigned.') if stock_use_rule: params.update({'stock_use_rule': stock_use_rule}) else: raise Exception('stock_use_rule is not assigned.') if coupon_use_rule: params.update({'coupon_use_rule': coupon_use_rule}) else: raise Exception('coupon_use_rule is not assigned.') if stock_type: params.update({'stock_type': stock_type}) else: raise Exception('stock_type is not assigned.') if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') if no_cash: params.update({'no_cash': no_cash}) if comment: params.update({'comment': comment}) if pattern_info: params.update({'pattern_info': pattern_info}) if ext_info: params.update({'ext_info': ext_info}) path = '/v3/marketing/favor/coupon-stocks' return self._core.request(path, method=RequestType.POST, data=params) def marketing_favor_stock_start(self, stock_creator_mchid, stock_id): """激活代金券批次 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param stock_id: 批次号,示例值:'9856000' """ params = {} if stock_creator_mchid: params.update({'stock_creator_mchid': stock_creator_mchid}) else: raise Exception('stock_creator_mchid is not assigned.') if stock_id: path = '/v3/marketing/favor/stocks/%s/start' % stock_id else: raise Exception('stock_id is not assigned.') return self._core.request(path, method=RequestType.POST, data=params) def marketing_favor_stock_send(self, stock_id, openid, out_request_no, stock_creator_mchid, coupon_value=None, coupon_minimum=None, appid=None): """发放代金券批次 :param stock_id: 批次号。示例值:'9856000' :param openid: 用户openid,示例值:'2323dfsdf342342' :param out_request_no: 商户单据号,示例值: '89560002019101000121' :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param coupon_value: 指定面额发券,面额。示例值:100 :param coupon_minimum: 指定面额发券,券门槛。示例值:100 :param appid: 应用ID,可不填,默认传入初始化时的appid,示例值:'wx1234567890abcdef' """ params = {} if stock_id: params.update({'stock_id': stock_id}) else: raise Exception('stock_id is not assigned.') if openid: path = '/v3/marketing/favor/users/%s/coupons' % openid else: raise Exception('openid is not assigned.') if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') if stock_creator_mchid: params.update({'stock_creator_mchid': stock_creator_mchid}) else: raise Exception('stock_creator_mchid is not assigned.') if coupon_value: params.update({'coupon_value': coupon_value}) if coupon_minimum: params.update({'coupon_minimum': coupon_minimum}) params.update({'appid': appid or self._appid}) return self._core.request(path, method=RequestType.POST, data=params) def marketing_favor_stock_pause(self, stock_creator_mchid, stock_id): """暂停代金券批次 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param stock_id: 批次号,示例值:'9856000' """ params = {} if stock_creator_mchid: params.update({'stock_creator_mchid': stock_creator_mchid}) else: raise Exception('stock_creator_mchid is not assigned.') if stock_id: path = '/v3/marketing/favor/stocks/%s/pause' % stock_id else: raise Exception('stock_id is not assigned.') return self._core.request(path, method=RequestType.POST, data=params) def marketing_favor_stock_restart(self, stock_creator_mchid, stock_id): """重启代金券批次 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param stock_id: 批次号,示例值:'9856000' """ params = {} if stock_creator_mchid: params.update({'stock_creator_mchid': stock_creator_mchid}) else: raise Exception('stock_creator_mchid is not assigned.') if stock_id: path = '/v3/marketing/favor/stocks/%s/restart' % stock_id else: raise Exception('stock_id is not assigned.') return self._core.request(path, method=RequestType.POST, data=params) def marketing_favor_stock_list(self, stock_creator_mchid, offset=0, limit=10, create_start_time=None, create_end_time=None, status=None): """条件查询批次列表 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param offset: 分页页码,页码从0开始,默认第0页。示例值:0 :param limit: 分页大小,最大10。示例值:8 :param create_start_time: 起始创建时间,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE。示例值:'2015-05-20T13:29:35.120+08:00' :param create_end_time: 终止创建时间,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE。示例值:'2015-05-20T13:29:35.120+08:00' :param status: 批次状态,枚举值:'unactivated':未激活,'audit':审核中,'running':运行中,'stoped':已停止,'paused':暂停发放。示例值:'paused' """ if stock_creator_mchid: path = '/v3/marketing/favor/stocks?offset=%s&limit=%s&stock_creator_mchid=%s' % (offset, limit, stock_creator_mchid) else: raise Exception('stock_creator_mchid is not assigned.') if create_start_time: path = '%s&create_start_time=%s' % (path, create_start_time) if create_end_time: path = '%s&create_end_time=%s' % (path, create_end_time) if status: path = '%s&status=%s' % (path, status) return self._core.request(path) def marketing_favor_stock_detail(self, stock_creator_mchid, stock_id): """查询批次详情 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param stock_id: 批次号,示例值:'9856000' """ if stock_id: path = '/v3/marketing/favor/stocks/%s' % stock_id else: raise Exception('stock_id is not assigned.') if stock_creator_mchid: path = '%s?stock_creator_mchid=%s' % (path, stock_creator_mchid) else: raise Exception('stock_creator_mchid is not assigned.') return self._core.request(path) def marketing_favor_coupon_detail(self, coupon_id, openid): """查询代金券详情 :param coupon_id: 代金券id,示例值:'9856888' :param openid: 用户openid,示例值:'2323dfsdf342342' """ if coupon_id and openid: path = '/v3/marketing/favor/users/%s/coupons/%s?appid=%s' % (openid, coupon_id, self._appid) else: raise Exception('coupon_id or openid is not assigned.') return self._core.request(path) def marketing_favor_stock_merchant(self, stock_creator_mchid, stock_id, offset=0, limit=50): """查询代金券可用商户 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param stock_id: 批次号,示例值:'9856000' :param offset: 分页页码,最大1000。示例值: 10 :param limit: 分页大小,最大50。示例值: 10 """ if stock_id: path = '/v3/marketing/favor/stocks/%s/merchants' % stock_id else: raise Exception('stock_id is not assigned.') if stock_creator_mchid: path = '%s?stock_creator_mchid=%s&offset=%s&limit=%s&' % (path, stock_creator_mchid, offset, limit) return self._core.request(path) def marketing_favor_stock_item(self, stock_creator_mchid, stock_id, offset=0, limit=50): """查询代金券可用单品 :param stock_creator_mchid: 创建批次的商户号,示例值:'8956000' :param stock_id: 批次号,示例值:'9856000' :param offset: 分页页码,最大500。示例值: 10 :param limit: 分页大小,最大100。示例值: 10 """ if stock_id: path = '/v3/marketing/favor/stocks/%s/items' % stock_id else: raise Exception('stock_id is not assigned.') if stock_creator_mchid: path = '%s?stock_creator_mchid=%s&offset=%s&limit=%s&' % (path, stock_creator_mchid, offset, limit) return self._core.request(path) def marketing_favor_user_coupon(self, openid, stock_id=None, status=None, creator_mchid=None, sender_mchid=None, available_mchid=None, offset=0, limit=20): """根据商户号查用户的券 :param openid: 用户openid,示例值:'2323dfsdf342342' :param stock_id: 批次号,示例值:'9856000' :param status: 券状态,代金券状态:'SENDED':可用,'USED':已实扣,填写available_mchid参数则该字段不生效。示例值:'USED' :param creator_mchid: 创建批次的商户号.示例值:'9865002' :param sender_mchid: 批次发放商户号。示例值:'9865001' :param available_mchid: 可用商户号。示例值: '9865000' :param offset: 分页页码,默认0,填写available_mchid,该字段不生效。示例值:0 :param limit: 分页大小,默认20,填写available_mchid,该字段不生效。示例值:20 """ if openid: path = '/v3/marketing/favor/users/%s/coupons?appid=%s&offset=%s&limit=%s' % (openid, self._appid, offset, limit) else: raise Exception('openid is not assigned.') if stock_id: path = '%s&stock_id=%s' % (path, stock_id) if status: path = '%s&status=%s' % (path, status) if creator_mchid: path = '%s&creator_mchid=%s' % (path, creator_mchid) elif sender_mchid: path = '%s&sender_mchid=%s' % (path, sender_mchid) elif available_mchid: path = '%s&available_mchid=%s' % (path, available_mchid) return self._core.request(path) def marketing_favor_use_flow(self, stock_id): """下载批次核销明细 :param stock_id: 批次号,微信为每个代金券批次分配的唯一id。示例值:'9865000' """ if stock_id: path = '/v3/marketing/favor/stocks/%s/use-flow' % stock_id else: raise Exception('stock_id is not assigned.') return self._core.request(path) def marketing_favor_refund_flow(self, stock_id): """下载批次退款明细 :param stock_id: 批次号,微信为每个代金券批次分配的唯一id。示例值:'9865000' """ if stock_id: path = '/v3/marketing/favor/stocks/%s/refund-flow' % stock_id else: raise Exception('stock_id is not assigned.') return self._core.request(path) def marketing_favor_callback_update(self, notify_url=None, switch=True, mchid=None): """设置消息通知地址 :param notify_url: 支付通知商户url地址。示例值:'https://pay.weixin.qq.com' :param switch: 回调开关,枚举值:True:开启推送,False:停止推送。示例值:True :param mchid: 微信支付商户号,可不填,默认传入初始化的mchid。示例值:'9856888' """ params = {} params.update({'mchid': mchid or self._mchid}) if not (notify_url or self._notify_url): raise Exception('notify_url is not assigned.') params.update({'notify_url': notify_url or self._notify_url}) params.update({'switch': switch}) path = '/v3/marketing/favor/callbacks' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_stock_create(self, stock_name, belong_merchant, goods_name, stock_type, coupon_use_rule, stock_send_rule, out_request_no, coupon_code_mode, comment=None, custom_entrance=None, display_pattern_info=None, notify_config=None, subsidy=False): """创建商家券 :params stock_name: 商家券批次名称,字数上限为21个,一个中文汉字/英文字母/数字均占用一个字数。示例值:'8月1日活动券' :params belong_merchant: 批次归属商户号。注:普通直连模式,该参数为直连商户号。示例值:'10000022' :params goods_name: 适用商品范围,用来描述批次在哪些商品可用,会显示在微信卡包中。字数上限为15个。示例值:'xxx商品使用' :params stock_type: 批次类型,'NORMAL':固定面额满减券批次,'DISCOUNT':折扣券批次,'EXCHANGE':换购券批次。示例值:'NORMAL' :params coupon_use_rule: 核销规则。示例值:{'coupon_available_time':{}, 'fixed_normal_coupon':{}, 'use_method':'OFF_LINE', } :params stock_send_rule: 发放规则。示例值:{'max_coupons':100, 'max_coupons_per_user':5} :params out_request_no: 商户请求单号。示例值:'100002322019090134234sfdf' :params coupon_code_mode: 券code模式,枚举值:'WECHATPAY_MODE':系统分配券code。(固定22位纯数字),'MERCHANT_API':商户发放时接口指定券code,'MERCHANT_UPLOAD':商户上传自定义code,发券时系统随机选取上传的券code。示例值:'WECHATPAY_MODE' :params comment: 批次备注,仅配置商户可见,用于自定义信息。字数上限为20个。示例值:'活动使用' :params custom_entrance: 自定义入口。示例值:{'hall_id':'233455656'} :params display_pattern_info: 样式信息。示例值:{'description':'xxx门店可用'} :params notify_config: 事件通知配置。示例值:{'notify_appid':'wx23232232323'} :params subsidy=False: 是否允许营销补贴,该批次发放的券是否允许进行补差。示例值:False """ params = {} if stock_name: params.update({'stock_name': stock_name}) else: raise Exception('stock_name is not assigned.') if belong_merchant: params.update({'belong_merchant': belong_merchant}) else: raise Exception('belong_merchant is not assigned.') if goods_name: params.update({'goods_name': goods_name}) else: raise Exception('goods_name is not assigned.') if stock_type: params.update({'stock_type': stock_type}) else: raise Exception('stock_type is not assigned.') if coupon_use_rule: params.update({'coupon_use_rule': coupon_use_rule}) else: raise Exception('coupon_use_rule is not assigned.') if stock_send_rule: params.update({'stock_send_rule': stock_send_rule}) else: raise Exception('stock_send_rule is not assigned.') if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') if coupon_code_mode: params.update({'coupon_code_mode': coupon_code_mode}) else: raise Exception('coupon_code_mode is not assigned.') if comment: params.update({'comment': comment}) if custom_entrance: params.update({'custom_entrance': custom_entrance}) if display_pattern_info: params.update({'display_pattern_info': display_pattern_info}) if notify_config: params.update({'notify_config': notify_config}) params.update({'subsidy': subsidy}) path = '/v3/marketing/busifavor/stocks' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_stock_query(self, stock_id): """查询商家券详情 :param stock_id: 批次号。示例值:1212 """ if stock_id: path = '/v3/marketing/busifavor/stocks/%s' % stock_id else: raise Exception('stock_id is not assigned.') return self._core.request(path) def marketing_busifavor_coupon_use(self, coupon_code, use_time, use_request_no, stock_id=None, openid=None, appid=None): """核销用户券 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param use_time: 请求核销时间,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE。示例值:'2015-05-20T13:29:35+08:00' :param use_request_no: 核销请求单据号,每次核销请求的唯一标识,商户需保证唯一。示例值:'1002600620019090123143254435' :param stock_id: 批次号。示例值:1212 :param openid: 用户标识。示例值:'xsd3434454567676' :param appid: 应用ID,可不填,默认传入初始化时的appid,示例值:'wx1234567890abcdef' """ params = {} if coupon_code: params.update({'coupon_code': coupon_code}) else: raise Exception('coupon_code is not assigned.') if use_time: params.update({'use_time': use_time}) else: raise Exception('use_time is not assigned.') if use_request_no: params.update({'use_request_no': use_request_no}) else: raise Exception('use_request_no is not assigned.') if stock_id: params.update({'stock_id': stock_id}) if openid: params.update({'openid': openid}) params.update({'appid': appid or self._appid}) path = '/v3/marketing/busifavor/coupons/use' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_user_coupon(self, openid, stock_id=None, coupon_state=None, creator_merchant=None, belong_merchant=None, sender_merchant=None, offset=0, limit=20): """根据过滤条件查询用户券 :param openid: 用户标识。示例值:'xsd3434454567676' :param stock_id: 批次号。示例值:1212 :param coupon_state: 券状态,枚举值:'SENDED':可用,'USED':已核销,'EXPIRED':已过期,示例值:'SENDED' :param creator_merchant: 创建批次的商户号。示例值:'1000000001' :param belong_merchant: 批次归属商户号。示例值:'1000000002' :param sender_merchant: 批次发放商户号。示例值:'1000000003' :param offset: 分页页码。示例值:0 :param limit: 分页大小。示例值:20 """ if openid: path = '/v3/marketing/busifavor/users/%s/coupons?appid=%s&offset=%s&limit=%s' % (openid, self._appid, offset, limit) else: raise Exception('openid is not assigned.') if stock_id: path = '%s&stock_id=%s' % (path, stock_id) if coupon_state: path = '%s&coupon_state=%s' % (path, coupon_state) if creator_merchant: path = '%s&creator_merchant=%s' % (path, creator_merchant) if belong_merchant: path = '%s&belong_merchant=%s' % (path, belong_merchant) if sender_merchant: path = '%s&sender_merchant=%s' % (path, sender_merchant) return self._core.request(path) def marketing_busifavor_coupon_detail(self, coupon_code, openid): """查询用户单张券详情 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param openid: 用户标识。示例值:'xsd3434454567676' """ if not (coupon_code and openid): raise Exception('coupon_code or openid is not assigned.') path = '/v3/marketing/busifavor/users/%s/coupons/%s/appids/%s' % (openid, coupon_code, self._appid) return self._core.request(path) def marketing_busifavor_couponcode_upload(self, stock_id, upload_request_no, coupon_code_list=[]): """上传预存code :param stock_id: 批次号。示例值:1212 :param upload_request_no: 请求业务单据号。商户上传code的凭据号,商户侧需保持唯一性。示例值:'100002322019090134234sfdf' :param coupon_code_list: 券code列表。示例值:['ABC9588200','ABC9588201'] """ params = {} if stock_id: path = '/v3/marketing/busifavor/stocks/%s/couponcodes' % stock_id else: raise Exception('stock_id is not assigned.') if upload_request_no: params.update({'upload_request_no': upload_request_no}) else: raise Exception('upload_request_no is not assigned.') if coupon_code_list: params.update({'coupon_code_list': coupon_code_list}) return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_callback_update(self, mchid=None, notify_url=None): """设置商家券事件通知地址 :param mchid: 商户号,可不填,默认传入初始化的mchid。示例值:'10000098' :param notify_url: 通知URL地址,用于接收商家券事件通知的url地址,不填默认使用初始化的notify_url。示例值:'https://pay.weixin.qq.com' """ params = {} params.update({'mchid': mchid or self._mchid}) if not (notify_url or self._notify_url): raise Exception('notify_url is not assigned.') params.update({'notify_url': notify_url or self._notify_url}) path = '/v3/marketing/busifavor/callbacks' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_callback_query(self, mchid=None): """查询商家券事件通知地址 :param mchid: 商户号,不填默认使用初始化的mchid。示例值:'10000098' """ path = '/v3/marketing/busifavor/callbacks?mchid=%s' % (mchid or self._mchid) return self._core.request(path) def marketing_busifavor_coupon_associate(self, stock_id, coupon_code, out_trade_no, out_request_no): """关联订单信息 :param stock_id: 批次号。示例值:1212 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param out_trade_no: 关联的商户订单号,微信支付下单时的商户订单号,欲与该商家券关联的微信支付。示例值:'MCH_102233445' :param out_request_no: 商户请求单号,示例值:'1002600620019090123143254435' """ params = {} if stock_id: params.update({'stock_id': stock_id}) else: raise Exception('stock_id is not assigned.') if coupon_code: params.update({'coupon_code': coupon_code}) else: raise Exception('coupon_code is not assigned.') if out_trade_no: params.update({'out_trade_no': out_trade_no}) else: raise Exception('out_trade_no is not assigned.') if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') path = '/v3/marketing/busifavor/coupons/associate' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_coupon_disassociate(self, stock_id, coupon_code, out_trade_no, out_request_no): """取消关联订单信息 :param stock_id: 批次号。示例值:1212 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param out_trade_no: 关联的商户订单号,微信支付下单时的商户订单号,欲与该商家券关联的微信支付。示例值:'MCH_102233445' :param out_request_no: 商户请求单号,示例值:'1002600620019090123143254435' """ params = {} if stock_id: params.update({'stock_id': stock_id}) else: raise Exception('stock_id is not assigned.') if coupon_code: params.update({'coupon_code': coupon_code}) else: raise Exception('coupon_code is not assigned.') if out_trade_no: params.update({'out_trade_no': out_trade_no}) else: raise Exception('out_trade_no is not assigned.') if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') path = '/v3/marketing/busifavor/coupons/disassociate' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_stock_budget(self, stock_id, modify_budget_request_no, target_max_coupons=None, target_max_coupons_by_day=None, current_max_coupons=None, current_max_coupons_by_day=None): """修改批次预算 :param stock_id: 批次号。示例值:1212 :param modify_budget_request_no: 修改预算请求单据号,示例值:'1002600620019090123143254436' :param target_max_coupons: 目标批次最大发放个数。示例值:3000 :param target_max_coupons_by_day: 目标单天发放上限个数。示例值:500 :param current_max_coupons: 当前批次最大发放个数。示例值:500 :param current_max_coupons_by_day: 当前单天发放上限个数。示例值:300 """ params = {} if stock_id: path = '/v3/marketing/busifavor/stocks/%s/budget' % stock_id else: raise Exception('stock_id is not assigned.') if modify_budget_request_no: params.update({'modify_budget_request_no': modify_budget_request_no}) else: raise Exception('modify_budget_request_no is not assigned.') if target_max_coupons: params.update({'target_max_coupons': target_max_coupons}) elif target_max_coupons_by_day: params.update({'target_max_coupons_by_day': target_max_coupons_by_day}) else: raise Exception('target_max_coupons or target_max_coupons_by_day is not assigned.') if current_max_coupons: params.update({'current_max_coupons': current_max_coupons}) if current_max_coupons_by_day: params.update({'current_max_coupons_by_day': current_max_coupons_by_day}) return self._core.request(path, method=RequestType.PATCH, data=params) def marketing_busifavor_stock_modify(self, stock_id, out_request_no, custom_entrance=None, comment=None, goods_name=None, display_pattern_info=None, coupon_use_rule=None, stock_send_rule=None, notify_config=None): """修改商家券基本信息 :param stock_id: 批次号。示例值:1212 :param out_request_no: 商户请求单号,示例值:'1002600620019090123143254435' :param custom_entrance: 自定义入口。示例值:{'hall_id':'234567'} :param comment: 批次备注,字数上限为20个。示例值:'活动使用' :param goods_name: 适用商品范围。示例值:'xxx商品使用' :param display_pattern_info: 样式信息。示例值:{'description':'xxx门店可用'} :param coupon_use_rule: 核销规则。示例值:{'use_method':'OFF_LINE'} :param stock_send_rule: 发放规则。示例值:{'prevent_api_abuse':False} :param notify_config: 事件通知配置。示例值:{'notify_appid':'wx23232232323'} """ if stock_id: path = '/v3/marketing/busifavor/stocks/%s' % stock_id else: raise Exception('stock_id is not assigned.') params = {} if out_request_no: params.update({'out_request_no': out_request_no}) else: raise Exception('out_request_no is not assigned.') if custom_entrance: params.update({'custom_entrance': custom_entrance}) if comment: params.update({'comment': comment}) if goods_name: params.update({'goods_name': goods_name}) if display_pattern_info: params.update({'display_pattern_info': display_pattern_info}) if coupon_use_rule: params.update({'coupon_use_rule': coupon_use_rule}) if stock_send_rule: params.update({'stock_send_rule': stock_send_rule}) if notify_config: params.update({'notify_config': notify_config}) return self._core.request(path, method=RequestType.PATCH, data=params) def marketing_busifavor_coupon_return(self, coupon_code, stock_id, return_request_no): """申请退券 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param stock_id: 批次号。示例值:1212 :param return_request_no: 退券请求单据号。示例值:'1002600620019090123143254436' """ params = {} if coupon_code: params.update({'coupon_code': coupon_code}) else: raise Exception('coupon_code is not assigned.') if stock_id: params.update({'stock_id': stock_id}) else: raise Exception('stock_id is not assigned.') if return_request_no: params.update({'return_request_no': return_request_no}) else: raise Exception('return_request_no is not assigned.') path = '/v3/marketing/busifavor/coupons/return' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_coupon_deactivate(self, coupon_code, stock_id, deactivate_request_no, deactivate_reason=None): """使券失效 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param stock_id: 批次号。示例值:1212 :param deactivate_request_no: 失效请求单据号。示例值:'1002600620019090123143254436' :param deactivate_reason: 失效原因。示例值:'此券使用时间设置错误' """ params = {} if coupon_code: params.update({'coupon_code': coupon_code}) else: raise Exception('coupon_code is not assigned.') if stock_id: params.update({'stock_id': stock_id}) else: raise Exception('stock_id is not assigned.') if deactivate_request_no: params.update({'deactivate_request_no': deactivate_request_no}) else: raise Exception('deactivate_request_no is not assigned.') if deactivate_reason: params.update({'deactivate_reason': deactivate_reason}) path = '/v3/marketing/busifavor/coupons/deactivate' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_subsidy_pay(self, stock_id, coupon_code, transaction_id, payer_merchant, payee_merchant, amount, description, out_subsidy_no): """营销补差付款 :param stock_id: 批次号。示例值:1212 :param coupon_code: 券code,券的唯一标识。示例值:'sxxe34343434' :param transaction_id: 微信支付订单号。示例值:'4200000913202101152566792388' :param payer_merchant: 营销补差扣款商户号。示例值:'1900000001' :param payee_merchant: 营销补差入账商户号。示例值:'1900000002' :param amount: 补差付款金额。示例值:100 :param description: 补差付款描述。示例值:'20210115DESCRIPTION' :param out_subsidy_no: 业务请求唯一单号。示例值:'subsidy-abcd-12345678' """ params = {} if coupon_code: params.update({'coupon_code': coupon_code}) else: raise Exception('coupon_code is not assigned.') if stock_id: params.update({'stock_id': stock_id}) else: raise Exception('stock_id is not assigned.') if transaction_id: params.update({'transaction_id': transaction_id}) else: raise Exception('transaction_id is not assigned.') if payer_merchant: params.update({'payer_merchant': payer_merchant}) else: raise Exception('payer_merchant is not assigned.') if payee_merchant: params.update({'payee_merchant': payee_merchant}) else: raise Exception('payee_merchant is not assigned.') if amount: params.update({'amount': amount}) else: raise Exception('amount is not assigned.') if description: params.update({'description': description}) else: raise Exception('description is not assigned.') if out_subsidy_no: params.update({'out_subsidy_no': out_subsidy_no}) else: raise Exception('out_subsidy_no is not assigned.') path = '/v3/marketing/busifavor/subsidy/pay-receipts' return self._core.request(path, method=RequestType.POST, data=params) def marketing_busifavor_subsidy_query(self, subsidy_receipt_id): """查询营销补差付款单详情 :param subsidy_receipt_id: 补差付款单号。示例值:'1120200119165100000000000001' """ if subsidy_receipt_id: path = '/v3/marketing/busifavor/subsidy/pay-receipts/%s' % subsidy_receipt_id else: raise Exception('subsidy_receipt_id is not assigned.') return self._core.request(path) def industry_coupon_token(self, open_id, coupon_list=[]): """出行券切卡组件预下单 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_9_1.shtml :param open_id: 用户在商户AppID下的唯一标识,该用户为后续拉起切卡组件的用户。示例值:'obLatjrR8kUDlj4-nofQsPAJAAFI' :param coupon_list: 用户最近领取的出行券列表。示例值:[{"coupon_id": "11004999626", "stock_id": 16474341}] """ params = {} if open_id: params.update({'open_id': open_id}) else: raise Exception('open_id is not assigned.') if coupon_list: params.update({'coupon_list': coupon_list}) else: raise Exception('coupon_list is not assigned.') path = '/v3/industry-coupon/tokens' return self._core.request(path, method=RequestType.POST, data=params) def bank_package_file(self, package_id, bank_type, filepath): """导入定向用户协议号 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_8_1.shtml :package_id: 号码包唯一标识符。可在微信支付商户平台创建号码包后获得。示例值:'8473295' :filepath: 电子发票文件路径,只支持txt和csv两种格式,示例值:'./active_user.csv' """ if not (filepath and os.path.exists(filepath) and os.path.isfile(filepath)): raise Exception('filepath is not assigned or not exists') with open(filepath, mode='rb') as f: content = f.read() filename = os.path.basename(filepath) filetype = os.path.splitext(filename)[-1][1:].upper() mimes = { 'TXT': ' text/plain', 'CSV': 'text/csv' } if filetype not in mimes: raise Exception(f'wechatpayv3 does not support this file type: {filetype}') if not package_id or bank_type: raise Exception('package_id or bank_type is not assigned.') params = {} params.update({'meta': '{"bank_type":"%s", "filename":"%s", "sha256":"%s"}' % (bank_type, filename, sha256(content))}) files = [('file', (filename, content, mimes[filetype]))] path = '/v3/marketing/bank/packages/%s/tasks' % package_id return self._core.request(path, method=RequestType.POST, data=params, sign_data=params.get('meta'), files=files)