修改商品券批次组
更新时间:2025.11.27品牌方可以通过该接口同步修改商品券批次组内所有批次的基本信息,包括展示信息、通知配置。展示信息修改不会影响已经发放的用户商品券。
如果你需要调整组内批次的投放预算,请使用【修改商品券批次组发放预算API】
前置条件:
已创建
usage_mode为PROGRESSIVE_BUNDLE的商品券对应的批次组批次组内批次未失效或过期
频率限制:20/s
接口说明
支持商户:【品牌商户】
请求方式:【PATCH】/brand/marketing/product-coupon/product-coupons/{product_coupon_id}/stock-bundles/{stock_bundle_id}
请求域名:【主域名】https://api.mch.weixin.qq.com 使用该域名将访问就近的接入点
【备域名】https://api2.mch.weixin.qq.com 使用该域名将访问异地的接入点 ,指引点击查看
请求参数
Header HTTP头参数
Authorization 必填 string
请参考签名认证生成认证信息
Accept 必填 string
请设置为application/json
Content-Type 必填 string
请设置为application/json
Wechatpay-Serial 必填 string
【微信支付公钥ID】 请传入brand_id对应的微信支付公钥ID,接口将会校验两者的关联关系,参考微信支付公钥产品简介及使用说明获取微信支付公钥ID和相关的介绍。以下两种场景将使用到微信支付公钥: 1、接收到接口的返回内容,需要使用微信支付公钥进行验签; 2、调用含有敏感信息参数(如姓名、身份证号码)的接口时,需要使用微信支付公钥加密敏感信息后再传输参数,加密指引请参考微信支付公钥加密敏感信息指引。
path 路径参数
product_coupon_id 必填 string
【商品券ID】 商品券的唯一标识,创建商品券时由微信支付生成
stock_bundle_id 必填 string
【批次组ID】 商品券批次组的唯一标识,【创建商品券(多次优惠)】或【添加商品券批次组】时由微信支付生成,本接口会同步修改批次组内所有批次的基本信息,请确保该批次组属于 product_coupon_id 对应的商品券
body 包体参数
out_request_no 必填 string(40)
【修改请求单号】 品牌修改批次请求流水号,品牌侧需保持唯一性,可使用 数字、大小写字母、下划线_、短横线- 组成,长度在6-40个字符之间
remark 选填 string(20)
【备注】 仅配置品牌可见,用于自定义信息
usage_rule_display_info 选填 object
【券使用规则展示信息】 券使用规则展示信息
| 属性 | |||||
coupon_usage_method_list 必填 array[string] 【券使用方式列表】 可以配置多种使用方式 可选取值
mini_program_appid 选填 string 【小程序AppID】 品牌方小程序AppID,可在微信公众平台查看,该小程序与品牌存在绑定关系。 在 mini_program_path 选填 string 【小程序跳转路径】 品牌方小程序内部跳转路径。 在 app_path 选填 string 【APP跳转路径】 品牌方APP跳转路径,在 usage_description 必填 string(1000) 【券使用说明】 用于说明详细的券规则,长度不超过1000个UTF-8字符 coupon_available_store_info 选填 object 【券可用门店信息】 用于描述全部可用门店信息,可配置小程序跳转地址进行展示
|
coupon_display_info 选填 object
【用户商品券展示信息】 用户商品券在卡包中的展示详情,包括引导用户的自定义入口
| 属性 | |||||||||||||
code_display_mode 必填 string 【用户商品券Code展示模式】 决定用户商品券Code在卡包中的展示形态 可选取值
background_color 选填 string 【背景颜色】 券的背景颜色,可设置10种颜色,色值请参考下方说明。颜色取值为颜色图中的颜色名称,不填默认为
entrance_mini_program 选填 object 【小程序入口】 展示跳转小程序的入口
entrance_official_account 选填 object 【公众号入口】 展示跳转公众号的入口
entrance_finder 选填 object 【视频号入口】 展示跳转视频号的入口
|
notify_config 选填 object
【事件通知配置】 发生券相关事件时,微信支付会向品牌方发送通知,需要提供通知相关配置
| 属性 | |
notify_appid 必填 string 【事件通知AppID】 品牌的AppID,支持小程序、服务号、公众号、APP类型的AppID,可在微信公众平台查看,用于券事件通知时计算用户的OpenID,需要与品牌存在绑定关系 |
store_scope 选填 string
【可用门店范围】 控制该批次可以在品牌下哪些门店使用,不填默认设置为 NONE
可选取值
NONE: 无关联门店,该批次对外不展示可用门店信息ALL: 所有门店可用,该批次在品牌下的所有门店可用,品牌无需为该批次关联门店列表SPECIFIC: 特定门店可用,品牌需调用关联门店接口关联门店,关联后该批次在关联的门店可用
请求示例
PATCH
1curl -X PATCH \ 2 https://api.mch.weixin.qq.com/brand/marketing/product-coupon/product-coupons/200000001/stock-bundles/123456789 \ 3 -H "Authorization: WECHATPAY-BRAND-SHA256-RSA2048 brand_id=\"XXXX\",..." \ 4 -H "Accept: application/json" \ 5 -H "Wechatpay-Serial: PUB_KEY_ID_XXXX" \ 6 -H "Content-Type: application/json" \ 7 -d '{ 8 "out_request_no" : "34657_20250101_123456", 9 "remark" : "疯狂星期四项目专用", 10 "usage_rule_display_info" : { 11 "coupon_usage_method_list" : [ 12 "MINI_PROGRAM" 13 ], 14 "mini_program_appid" : "wx1234567890", 15 "mini_program_path" : "/pages/index/product", 16 "app_path" : "https://www.example.com/jump-to-app", 17 "usage_description" : "全场可用", 18 "coupon_available_store_info" : { 19 "description" : "可在上海市区的所有门店使用,详细列表参考小程序内信息为准", 20 "mini_program_appid" : "wx1234567890", 21 "mini_program_path" : "/pages/index/store-list" 22 } 23 }, 24 "coupon_display_info" : { 25 "code_display_mode" : "QRCODE", 26 "background_color" : "Color010", 27 "entrance_mini_program" : { 28 "appid" : "wx1234567890", 29 "path" : "/pages/index/product", 30 "entrance_wording" : "欢迎选购", 31 "guidance_wording" : "获取更多优惠" 32 }, 33 "entrance_official_account" : { 34 "appid" : "wx1234567890" 35 }, 36 "entrance_finder" : { 37 "finder_id" : "gh_12345678", 38 "finder_video_id" : "UDFsdf24df34dD456Hdf34", 39 "finder_video_cover_image_url" : "https://wxpaylogo.qpic.cn/wxpaylogo/xxxxx/xxx" 40 } 41 }, 42 "notify_config" : { 43 "notify_appid" : "wx4fd12345678" 44 }, 45 "store_scope" : "SPECIFIC" 46 }' 47
需配合微信支付工具库 WXPayUtility 使用,请参考Java
1package com.java.demo; 2 3import com.java.utils.WXPayBrandUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/brand/4015826861 4 5import com.google.gson.annotations.SerializedName; 6import com.google.gson.annotations.Expose; 7import okhttp3.MediaType; 8import okhttp3.OkHttpClient; 9import okhttp3.Request; 10import okhttp3.RequestBody; 11import okhttp3.Response; 12 13import java.io.IOException; 14import java.io.UncheckedIOException; 15import java.security.PrivateKey; 16import java.security.PublicKey; 17import java.util.ArrayList; 18import java.util.HashMap; 19import java.util.List; 20import java.util.Map; 21 22/** 23 * 修改商品券批次组 24 */ 25public class UpdateStockBundle { 26 private static String HOST = "https://api.mch.weixin.qq.com"; 27 private static String METHOD = "PATCH"; 28 private static String PATH = "/brand/marketing/product-coupon/product-coupons/{product_coupon_id}/stock-bundles/{stock_bundle_id}"; 29 30 public static void main(String[] args) { 31 // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/brand/4015415289 32 UpdateStockBundle client = new UpdateStockBundle( 33 "xxxxxxxx", // 品牌ID,是由微信支付系统生成并分配给每个品牌方的唯一标识符,品牌ID获取方式参考 https://pay.weixin.qq.com/doc/brand/4015415289 34 "1DDE55AD98Exxxxxxxxxx", // 品牌API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/brand/4015407570 35 "/path/to/apiclient_key.pem", // 品牌API证书私钥文件路径,本地文件路径 36 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/brand/4015453439 37 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 38 ); 39 40 UpdateStockBundleRequest request = new UpdateStockBundleRequest(); 41 request.productCouponId = "200000001"; 42 request.stockBundleId = "123456789"; 43 request.outRequestNo = "34657_20250101_123456"; 44 request.remark = "疯狂星期四项目专用"; 45 request.usageRuleDisplayInfo = new UsageRuleDisplayInfo(); 46 request.usageRuleDisplayInfo.couponUsageMethodList = new ArrayList<>(); 47 { 48 request.usageRuleDisplayInfo.couponUsageMethodList.add(CouponUsageMethod.OFFLINE); 49 }; 50 request.usageRuleDisplayInfo.miniProgramAppid = "wx1234567890"; 51 request.usageRuleDisplayInfo.miniProgramPath = "/pages/index/product"; 52 request.usageRuleDisplayInfo.appPath = "https://www.example.com/jump-to-app"; 53 request.usageRuleDisplayInfo.usageDescription = "全场可用"; 54 request.usageRuleDisplayInfo.couponAvailableStoreInfo = new CouponAvailableStoreInfo(); 55 request.usageRuleDisplayInfo.couponAvailableStoreInfo.description = "可在上海市区的所有门店使用,详细列表参考小程序内信息为准"; 56 request.usageRuleDisplayInfo.couponAvailableStoreInfo.miniProgramAppid = "wx1234567890"; 57 request.usageRuleDisplayInfo.couponAvailableStoreInfo.miniProgramPath = "/pages/index/store-list"; 58 request.couponDisplayInfo = new CouponDisplayInfo(); 59 request.couponDisplayInfo.codeDisplayMode = CouponCodeDisplayMode.QRCODE; 60 request.couponDisplayInfo.backgroundColor = "Color010"; 61 request.couponDisplayInfo.entranceMiniProgram = new EntranceMiniProgram(); 62 request.couponDisplayInfo.entranceMiniProgram.appid = "wx1234567890"; 63 request.couponDisplayInfo.entranceMiniProgram.path = "/pages/index/product"; 64 request.couponDisplayInfo.entranceMiniProgram.entranceWording = "欢迎选购"; 65 request.couponDisplayInfo.entranceMiniProgram.guidanceWording = "获取更多优惠"; 66 request.couponDisplayInfo.entranceOfficialAccount = new EntranceOfficialAccount(); 67 request.couponDisplayInfo.entranceOfficialAccount.appid = "wx1234567890"; 68 request.couponDisplayInfo.entranceFinder = new EntranceFinder(); 69 request.couponDisplayInfo.entranceFinder.finderId = "gh_12345678"; 70 request.couponDisplayInfo.entranceFinder.finderVideoId = "UDFsdf24df34dD456Hdf34"; 71 request.couponDisplayInfo.entranceFinder.finderVideoCoverImageUrl = "https://wxpaylogo.qpic.cn/wxpaylogo/xxxxx/xxx"; 72 request.notifyConfig = new NotifyConfig(); 73 request.notifyConfig.notifyAppid = "wx4fd12345678"; 74 request.storeScope = StockStoreScope.SPECIFIC; 75 try { 76 StockBundleEntity response = client.run(request); 77 // TODO: 请求成功,继续业务逻辑 78 System.out.println(response); 79 } catch (WXPayBrandUtility.ApiException e) { 80 // TODO: 请求失败,根据状态码执行不同的逻辑 81 e.printStackTrace(); 82 } 83 } 84 85 public StockBundleEntity run(UpdateStockBundleRequest request) { 86 String uri = PATH; 87 uri = uri.replace("{product_coupon_id}", WXPayBrandUtility.urlEncode(request.productCouponId)); 88 uri = uri.replace("{stock_bundle_id}", WXPayBrandUtility.urlEncode(request.stockBundleId)); 89 String reqBody = WXPayBrandUtility.toJson(request); 90 91 Request.Builder reqBuilder = new Request.Builder().url(HOST + uri); 92 reqBuilder.addHeader("Accept", "application/json"); 93 reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId); 94 reqBuilder.addHeader("Authorization", WXPayBrandUtility.buildAuthorization(brand_id, certificateSerialNo,privateKey, METHOD, uri, reqBody)); 95 reqBuilder.addHeader("Content-Type", "application/json"); 96 RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody); 97 reqBuilder.method(METHOD, requestBody); 98 Request httpRequest = reqBuilder.build(); 99 100 // 发送HTTP请求 101 OkHttpClient client = new OkHttpClient.Builder().build(); 102 try (Response httpResponse = client.newCall(httpRequest).execute()) { 103 String respBody = WXPayBrandUtility.extractBody(httpResponse); 104 if (httpResponse.code() >= 200 && httpResponse.code() < 300) { 105 // 2XX 成功,验证应答签名 106 WXPayBrandUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey, 107 httpResponse.headers(), respBody); 108 109 // 从HTTP应答报文构建返回数据 110 return WXPayBrandUtility.fromJson(respBody, StockBundleEntity.class); 111 } else { 112 throw new WXPayBrandUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers()); 113 } 114 } catch (IOException e) { 115 throw new UncheckedIOException("Sending request to " + uri + " failed.", e); 116 } 117 } 118 119 private final String brand_id; 120 private final String certificateSerialNo; 121 private final PrivateKey privateKey; 122 private final String wechatPayPublicKeyId; 123 private final PublicKey wechatPayPublicKey; 124 125 public UpdateStockBundle(String brand_id, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) { 126 this.brand_id = brand_id; 127 this.certificateSerialNo = certificateSerialNo; 128 this.privateKey = WXPayBrandUtility.loadPrivateKeyFromPath(privateKeyFilePath); 129 this.wechatPayPublicKeyId = wechatPayPublicKeyId; 130 this.wechatPayPublicKey = WXPayBrandUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath); 131 } 132 133 public static class UpdateStockBundleRequest { 134 @SerializedName("out_request_no") 135 public String outRequestNo; 136 137 @SerializedName("product_coupon_id") 138 @Expose(serialize = false) 139 public String productCouponId; 140 141 @SerializedName("stock_bundle_id") 142 @Expose(serialize = false) 143 public String stockBundleId; 144 145 @SerializedName("remark") 146 public String remark; 147 148 @SerializedName("usage_rule_display_info") 149 public UsageRuleDisplayInfo usageRuleDisplayInfo; 150 151 @SerializedName("coupon_display_info") 152 public CouponDisplayInfo couponDisplayInfo; 153 154 @SerializedName("notify_config") 155 public NotifyConfig notifyConfig; 156 157 @SerializedName("store_scope") 158 public StockStoreScope storeScope; 159 } 160 161 public static class StockBundleEntity { 162 @SerializedName("stock_bundle_id") 163 public String stockBundleId; 164 165 @SerializedName("stock_list") 166 public List<StockEntityInBundle> stockList = new ArrayList<StockEntityInBundle>(); 167 } 168 169 public static class UsageRuleDisplayInfo { 170 @SerializedName("coupon_usage_method_list") 171 public List<CouponUsageMethod> couponUsageMethodList = new ArrayList<CouponUsageMethod>(); 172 173 @SerializedName("mini_program_appid") 174 public String miniProgramAppid; 175 176 @SerializedName("mini_program_path") 177 public String miniProgramPath; 178 179 @SerializedName("app_path") 180 public String appPath; 181 182 @SerializedName("usage_description") 183 public String usageDescription; 184 185 @SerializedName("coupon_available_store_info") 186 public CouponAvailableStoreInfo couponAvailableStoreInfo; 187 } 188 189 public static class CouponDisplayInfo { 190 @SerializedName("code_display_mode") 191 public CouponCodeDisplayMode codeDisplayMode; 192 193 @SerializedName("background_color") 194 public String backgroundColor; 195 196 @SerializedName("entrance_mini_program") 197 public EntranceMiniProgram entranceMiniProgram; 198 199 @SerializedName("entrance_official_account") 200 public EntranceOfficialAccount entranceOfficialAccount; 201 202 @SerializedName("entrance_finder") 203 public EntranceFinder entranceFinder; 204 } 205 206 public static class NotifyConfig { 207 @SerializedName("notify_appid") 208 public String notifyAppid; 209 } 210 211 public enum StockStoreScope { 212 @SerializedName("NONE") 213 NONE, 214 @SerializedName("ALL") 215 ALL, 216 @SerializedName("SPECIFIC") 217 SPECIFIC 218 } 219 220 public static class StockEntityInBundle { 221 @SerializedName("product_coupon_id") 222 public String productCouponId; 223 224 @SerializedName("stock_id") 225 public String stockId; 226 227 @SerializedName("remark") 228 public String remark; 229 230 @SerializedName("coupon_code_mode") 231 public CouponCodeMode couponCodeMode; 232 233 @SerializedName("coupon_code_count_info") 234 public CouponCodeCountInfo couponCodeCountInfo; 235 236 @SerializedName("stock_send_rule") 237 public StockSendRule stockSendRule; 238 239 @SerializedName("progressive_bundle_usage_rule") 240 public StockUsageRule progressiveBundleUsageRule; 241 242 @SerializedName("stock_bundle_info") 243 public StockBundleInfo stockBundleInfo; 244 245 @SerializedName("usage_rule_display_info") 246 public UsageRuleDisplayInfo usageRuleDisplayInfo; 247 248 @SerializedName("coupon_display_info") 249 public CouponDisplayInfo couponDisplayInfo; 250 251 @SerializedName("notify_config") 252 public NotifyConfig notifyConfig; 253 254 @SerializedName("store_scope") 255 public StockStoreScope storeScope; 256 257 @SerializedName("sent_count_info") 258 public StockSentCountInfo sentCountInfo; 259 260 @SerializedName("state") 261 public StockState state; 262 263 @SerializedName("deactivate_request_no") 264 public String deactivateRequestNo; 265 266 @SerializedName("deactivate_time") 267 public String deactivateTime; 268 269 @SerializedName("deactivate_reason") 270 public String deactivateReason; 271 } 272 273 public enum CouponUsageMethod { 274 @SerializedName("OFFLINE") 275 OFFLINE, 276 @SerializedName("MINI_PROGRAM") 277 MINI_PROGRAM, 278 @SerializedName("APP") 279 APP, 280 @SerializedName("PAYMENT_CODE") 281 PAYMENT_CODE 282 } 283 284 public static class CouponAvailableStoreInfo { 285 @SerializedName("description") 286 public String description; 287 288 @SerializedName("mini_program_appid") 289 public String miniProgramAppid; 290 291 @SerializedName("mini_program_path") 292 public String miniProgramPath; 293 } 294 295 public enum CouponCodeDisplayMode { 296 @SerializedName("INVISIBLE") 297 INVISIBLE, 298 @SerializedName("BARCODE") 299 BARCODE, 300 @SerializedName("QRCODE") 301 QRCODE 302 } 303 304 public static class EntranceMiniProgram { 305 @SerializedName("appid") 306 public String appid; 307 308 @SerializedName("path") 309 public String path; 310 311 @SerializedName("entrance_wording") 312 public String entranceWording; 313 314 @SerializedName("guidance_wording") 315 public String guidanceWording; 316 } 317 318 public static class EntranceOfficialAccount { 319 @SerializedName("appid") 320 public String appid; 321 } 322 323 public static class EntranceFinder { 324 @SerializedName("finder_id") 325 public String finderId; 326 327 @SerializedName("finder_video_id") 328 public String finderVideoId; 329 330 @SerializedName("finder_video_cover_image_url") 331 public String finderVideoCoverImageUrl; 332 } 333 334 public enum CouponCodeMode { 335 @SerializedName("WECHATPAY") 336 WECHATPAY, 337 @SerializedName("UPLOAD") 338 UPLOAD 339 } 340 341 public static class CouponCodeCountInfo { 342 @SerializedName("total_count") 343 public Long totalCount; 344 345 @SerializedName("available_count") 346 public Long availableCount; 347 } 348 349 public static class StockSendRule { 350 @SerializedName("max_count") 351 public Long maxCount; 352 353 @SerializedName("max_count_per_day") 354 public Long maxCountPerDay; 355 356 @SerializedName("max_count_per_user") 357 public Long maxCountPerUser; 358 } 359 360 public static class StockUsageRule { 361 @SerializedName("coupon_available_period") 362 public CouponAvailablePeriod couponAvailablePeriod; 363 364 @SerializedName("normal_coupon") 365 public NormalCouponUsageRule normalCoupon; 366 367 @SerializedName("discount_coupon") 368 public DiscountCouponUsageRule discountCoupon; 369 370 @SerializedName("exchange_coupon") 371 public ExchangeCouponUsageRule exchangeCoupon; 372 } 373 374 public static class StockBundleInfo { 375 @SerializedName("stock_bundle_id") 376 public String stockBundleId; 377 378 @SerializedName("stock_bundle_index") 379 public Long stockBundleIndex; 380 } 381 382 public static class StockSentCountInfo { 383 @SerializedName("total_count") 384 public Long totalCount; 385 386 @SerializedName("today_count") 387 public Long todayCount; 388 } 389 390 public enum StockState { 391 @SerializedName("AUDITING") 392 AUDITING, 393 @SerializedName("SENDING") 394 SENDING, 395 @SerializedName("PAUSED") 396 PAUSED, 397 @SerializedName("STOPPED") 398 STOPPED, 399 @SerializedName("DEACTIVATED") 400 DEACTIVATED 401 } 402 403 public static class CouponAvailablePeriod { 404 @SerializedName("available_begin_time") 405 public String availableBeginTime; 406 407 @SerializedName("available_end_time") 408 public String availableEndTime; 409 410 @SerializedName("available_days") 411 public Long availableDays; 412 413 @SerializedName("wait_days_after_receive") 414 public Long waitDaysAfterReceive; 415 416 @SerializedName("weekly_available_period") 417 public FixedWeekPeriod weeklyAvailablePeriod; 418 419 @SerializedName("irregular_available_period_list") 420 public List<TimePeriod> irregularAvailablePeriodList; 421 } 422 423 public static class NormalCouponUsageRule { 424 @SerializedName("threshold") 425 public Long threshold; 426 427 @SerializedName("discount_amount") 428 public Long discountAmount; 429 } 430 431 public static class DiscountCouponUsageRule { 432 @SerializedName("threshold") 433 public Long threshold; 434 435 @SerializedName("percent_off") 436 public Long percentOff; 437 } 438 439 public static class ExchangeCouponUsageRule { 440 @SerializedName("threshold") 441 public Long threshold; 442 443 @SerializedName("exchange_price") 444 public Long exchangePrice; 445 } 446 447 public static class FixedWeekPeriod { 448 @SerializedName("day_list") 449 public List<WeekEnum> dayList; 450 451 @SerializedName("day_period_list") 452 public List<PeriodOfTheDay> dayPeriodList; 453 } 454 455 public static class TimePeriod { 456 @SerializedName("begin_time") 457 public String beginTime; 458 459 @SerializedName("end_time") 460 public String endTime; 461 } 462 463 public enum WeekEnum { 464 @SerializedName("MONDAY") 465 MONDAY, 466 @SerializedName("TUESDAY") 467 TUESDAY, 468 @SerializedName("WEDNESDAY") 469 WEDNESDAY, 470 @SerializedName("THURSDAY") 471 THURSDAY, 472 @SerializedName("FRIDAY") 473 FRIDAY, 474 @SerializedName("SATURDAY") 475 SATURDAY, 476 @SerializedName("SUNDAY") 477 SUNDAY 478 } 479 480 public static class PeriodOfTheDay { 481 @SerializedName("begin_time") 482 public Long beginTime; 483 484 @SerializedName("end_time") 485 public Long endTime; 486 } 487 488} 489
需配合微信支付工具库 wxpay_utility 使用,请参考Go
1package main 2 3import ( 4 "bytes" 5 "demo/wxpay_brand_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/brand/4015826866 6 "encoding/json" 7 "fmt" 8 "net/http" 9 "net/url" 10 "strings" 11 "time" 12) 13 14func main() { 15 // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/brand/4015415289 16 config, err := wxpay_brand_utility.CreateBrandConfig( 17 "xxxxxxxx", // 品牌ID,是由微信支付系统生成并分配给每个品牌方的唯一标识符,品牌ID获取方式参考 https://pay.weixin.qq.com/doc/brand/4015415289 18 "1DDE55AD98Exxxxxxxxxx", // 品牌API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/brand/4015407570 19 "/path/to/apiclient_key.pem", // 品牌API证书私钥文件路径,本地文件路径 20 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/brand/4015453439 21 "/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径 22 ) 23 if err != nil { 24 fmt.Println(err) 25 return 26 } 27 28 request := &UpdateStockBundleRequest{ 29 ProductCouponId: wxpay_brand_utility.String("200000001"), 30 StockBundleId: wxpay_brand_utility.String("123456789"), 31 OutRequestNo: wxpay_brand_utility.String("34657_20250101_123456"), 32 Remark: wxpay_brand_utility.String("疯狂星期四项目专用"), 33 UsageRuleDisplayInfo: &UsageRuleDisplayInfo{ 34 CouponUsageMethodList: []CouponUsageMethod{COUPONUSAGEMETHOD_OFFLINE}, 35 MiniProgramAppid: wxpay_brand_utility.String("wx1234567890"), 36 MiniProgramPath: wxpay_brand_utility.String("/pages/index/product"), 37 AppPath: wxpay_brand_utility.String("https://www.example.com/jump-to-app"), 38 UsageDescription: wxpay_brand_utility.String("全场可用"), 39 CouponAvailableStoreInfo: &CouponAvailableStoreInfo{ 40 Description: wxpay_brand_utility.String("可在上海市区的所有门店使用,详细列表参考小程序内信息为准"), 41 MiniProgramAppid: wxpay_brand_utility.String("wx1234567890"), 42 MiniProgramPath: wxpay_brand_utility.String("/pages/index/store-list"), 43 }, 44 }, 45 CouponDisplayInfo: &CouponDisplayInfo{ 46 CodeDisplayMode: COUPONCODEDISPLAYMODE_QRCODE.Ptr(), 47 BackgroundColor: wxpay_brand_utility.String("Color010"), 48 EntranceMiniProgram: &EntranceMiniProgram{ 49 Appid: wxpay_brand_utility.String("wx1234567890"), 50 Path: wxpay_brand_utility.String("/pages/index/product"), 51 EntranceWording: wxpay_brand_utility.String("欢迎选购"), 52 GuidanceWording: wxpay_brand_utility.String("获取更多优惠"), 53 }, 54 EntranceOfficialAccount: &EntranceOfficialAccount{ 55 Appid: wxpay_brand_utility.String("wx1234567890"), 56 }, 57 EntranceFinder: &EntranceFinder{ 58 FinderId: wxpay_brand_utility.String("gh_12345678"), 59 FinderVideoId: wxpay_brand_utility.String("UDFsdf24df34dD456Hdf34"), 60 FinderVideoCoverImageUrl: wxpay_brand_utility.String("https://wxpaylogo.qpic.cn/wxpaylogo/xxxxx/xxx"), 61 }, 62 }, 63 NotifyConfig: &NotifyConfig{ 64 NotifyAppid: wxpay_brand_utility.String("wx4fd12345678"), 65 }, 66 StoreScope: STOCKSTORESCOPE_SPECIFIC.Ptr(), 67 } 68 69 response, err := UpdateStockBundle(config, request) 70 if err != nil { 71 fmt.Printf("请求失败: %+v\n", err) 72 // TODO: 请求失败,根据状态码执行不同的处理 73 return 74 } 75 76 // TODO: 请求成功,继续业务逻辑 77 fmt.Printf("请求成功: %+v\n", response) 78} 79 80func UpdateStockBundle(config *wxpay_brand_utility.BrandConfig, request *UpdateStockBundleRequest) (response *StockBundleEntity, err error) { 81 const ( 82 host = "https://api.mch.weixin.qq.com" 83 method = "PATCH" 84 path = "/brand/marketing/product-coupon/product-coupons/{product_coupon_id}/stock-bundles/{stock_bundle_id}" 85 ) 86 87 reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path)) 88 if err != nil { 89 return nil, err 90 } 91 reqUrl.Path = strings.Replace(reqUrl.Path, "{product_coupon_id}", url.PathEscape(*request.ProductCouponId), -1) 92 reqUrl.Path = strings.Replace(reqUrl.Path, "{stock_bundle_id}", url.PathEscape(*request.StockBundleId), -1) 93 reqBody, err := json.Marshal(request) 94 if err != nil { 95 return nil, err 96 } 97 httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody)) 98 if err != nil { 99 return nil, err 100 } 101 httpRequest.Header.Set("Accept", "application/json") 102 httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId()) 103 httpRequest.Header.Set("Content-Type", "application/json") 104 authorization, err := wxpay_brand_utility.BuildAuthorization(config.BrandId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody) 105 if err != nil { 106 return nil, err 107 } 108 httpRequest.Header.Set("Authorization", authorization) 109 110 client := &http.Client{} 111 httpResponse, err := client.Do(httpRequest) 112 if err != nil { 113 return nil, err 114 } 115 respBody, err := wxpay_brand_utility.ExtractResponseBody(httpResponse) 116 if err != nil { 117 return nil, err 118 } 119 if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 { 120 // 2XX 成功,验证应答签名 121 err = wxpay_brand_utility.ValidateResponse( 122 config.WechatPayPublicKeyId(), 123 config.WechatPayPublicKey(), 124 &httpResponse.Header, 125 respBody, 126 ) 127 if err != nil { 128 return nil, err 129 } 130 response := &StockBundleEntity{} 131 if err := json.Unmarshal(respBody, response); err != nil { 132 return nil, err 133 } 134 135 return response, nil 136 } else { 137 return nil, wxpay_brand_utility.NewApiException( 138 httpResponse.StatusCode, 139 httpResponse.Header, 140 respBody, 141 ) 142 } 143} 144 145type UpdateStockBundleRequest struct { 146 OutRequestNo *string `json:"out_request_no,omitempty"` 147 ProductCouponId *string `json:"product_coupon_id,omitempty"` 148 StockBundleId *string `json:"stock_bundle_id,omitempty"` 149 Remark *string `json:"remark,omitempty"` 150 UsageRuleDisplayInfo *UsageRuleDisplayInfo `json:"usage_rule_display_info,omitempty"` 151 CouponDisplayInfo *CouponDisplayInfo `json:"coupon_display_info,omitempty"` 152 NotifyConfig *NotifyConfig `json:"notify_config,omitempty"` 153 StoreScope *StockStoreScope `json:"store_scope,omitempty"` 154} 155 156func (o *UpdateStockBundleRequest) MarshalJSON() ([]byte, error) { 157 type Alias UpdateStockBundleRequest 158 a := &struct { 159 ProductCouponId *string `json:"product_coupon_id,omitempty"` 160 StockBundleId *string `json:"stock_bundle_id,omitempty"` 161 *Alias 162 }{ 163 // 序列化时移除非 Body 字段 164 ProductCouponId: nil, 165 StockBundleId: nil, 166 Alias: (*Alias)(o), 167 } 168 return json.Marshal(a) 169} 170 171type StockBundleEntity struct { 172 StockBundleId *string `json:"stock_bundle_id,omitempty"` 173 StockList []StockEntityInBundle `json:"stock_list,omitempty"` 174} 175 176type UsageRuleDisplayInfo struct { 177 CouponUsageMethodList []CouponUsageMethod `json:"coupon_usage_method_list,omitempty"` 178 MiniProgramAppid *string `json:"mini_program_appid,omitempty"` 179 MiniProgramPath *string `json:"mini_program_path,omitempty"` 180 AppPath *string `json:"app_path,omitempty"` 181 UsageDescription *string `json:"usage_description,omitempty"` 182 CouponAvailableStoreInfo *CouponAvailableStoreInfo `json:"coupon_available_store_info,omitempty"` 183} 184 185type CouponDisplayInfo struct { 186 CodeDisplayMode *CouponCodeDisplayMode `json:"code_display_mode,omitempty"` 187 BackgroundColor *string `json:"background_color,omitempty"` 188 EntranceMiniProgram *EntranceMiniProgram `json:"entrance_mini_program,omitempty"` 189 EntranceOfficialAccount *EntranceOfficialAccount `json:"entrance_official_account,omitempty"` 190 EntranceFinder *EntranceFinder `json:"entrance_finder,omitempty"` 191} 192 193type NotifyConfig struct { 194 NotifyAppid *string `json:"notify_appid,omitempty"` 195} 196 197type StockStoreScope string 198 199func (e StockStoreScope) Ptr() *StockStoreScope { 200 return &e 201} 202 203const ( 204 STOCKSTORESCOPE_NONE StockStoreScope = "NONE" 205 STOCKSTORESCOPE_ALL StockStoreScope = "ALL" 206 STOCKSTORESCOPE_SPECIFIC StockStoreScope = "SPECIFIC" 207) 208 209type StockEntityInBundle struct { 210 ProductCouponId *string `json:"product_coupon_id,omitempty"` 211 StockId *string `json:"stock_id,omitempty"` 212 Remark *string `json:"remark,omitempty"` 213 CouponCodeMode *CouponCodeMode `json:"coupon_code_mode,omitempty"` 214 CouponCodeCountInfo *CouponCodeCountInfo `json:"coupon_code_count_info,omitempty"` 215 StockSendRule *StockSendRule `json:"stock_send_rule,omitempty"` 216 ProgressiveBundleUsageRule *StockUsageRule `json:"progressive_bundle_usage_rule,omitempty"` 217 StockBundleInfo *StockBundleInfo `json:"stock_bundle_info,omitempty"` 218 UsageRuleDisplayInfo *UsageRuleDisplayInfo `json:"usage_rule_display_info,omitempty"` 219 CouponDisplayInfo *CouponDisplayInfo `json:"coupon_display_info,omitempty"` 220 NotifyConfig *NotifyConfig `json:"notify_config,omitempty"` 221 StoreScope *StockStoreScope `json:"store_scope,omitempty"` 222 SentCountInfo *StockSentCountInfo `json:"sent_count_info,omitempty"` 223 State *StockState `json:"state,omitempty"` 224 DeactivateRequestNo *string `json:"deactivate_request_no,omitempty"` 225 DeactivateTime *time.Time `json:"deactivate_time,omitempty"` 226 DeactivateReason *string `json:"deactivate_reason,omitempty"` 227} 228 229type CouponUsageMethod string 230 231func (e CouponUsageMethod) Ptr() *CouponUsageMethod { 232 return &e 233} 234 235const ( 236 COUPONUSAGEMETHOD_OFFLINE CouponUsageMethod = "OFFLINE" 237 COUPONUSAGEMETHOD_MINI_PROGRAM CouponUsageMethod = "MINI_PROGRAM" 238 COUPONUSAGEMETHOD_APP CouponUsageMethod = "APP" 239 COUPONUSAGEMETHOD_PAYMENT_CODE CouponUsageMethod = "PAYMENT_CODE" 240) 241 242type CouponAvailableStoreInfo struct { 243 Description *string `json:"description,omitempty"` 244 MiniProgramAppid *string `json:"mini_program_appid,omitempty"` 245 MiniProgramPath *string `json:"mini_program_path,omitempty"` 246} 247 248type CouponCodeDisplayMode string 249 250func (e CouponCodeDisplayMode) Ptr() *CouponCodeDisplayMode { 251 return &e 252} 253 254const ( 255 COUPONCODEDISPLAYMODE_INVISIBLE CouponCodeDisplayMode = "INVISIBLE" 256 COUPONCODEDISPLAYMODE_BARCODE CouponCodeDisplayMode = "BARCODE" 257 COUPONCODEDISPLAYMODE_QRCODE CouponCodeDisplayMode = "QRCODE" 258) 259 260type EntranceMiniProgram struct { 261 Appid *string `json:"appid,omitempty"` 262 Path *string `json:"path,omitempty"` 263 EntranceWording *string `json:"entrance_wording,omitempty"` 264 GuidanceWording *string `json:"guidance_wording,omitempty"` 265} 266 267type EntranceOfficialAccount struct { 268 Appid *string `json:"appid,omitempty"` 269} 270 271type EntranceFinder struct { 272 FinderId *string `json:"finder_id,omitempty"` 273 FinderVideoId *string `json:"finder_video_id,omitempty"` 274 FinderVideoCoverImageUrl *string `json:"finder_video_cover_image_url,omitempty"` 275} 276 277type CouponCodeMode string 278 279func (e CouponCodeMode) Ptr() *CouponCodeMode { 280 return &e 281} 282 283const ( 284 COUPONCODEMODE_WECHATPAY CouponCodeMode = "WECHATPAY" 285 COUPONCODEMODE_UPLOAD CouponCodeMode = "UPLOAD" 286) 287 288type CouponCodeCountInfo struct { 289 TotalCount *int64 `json:"total_count,omitempty"` 290 AvailableCount *int64 `json:"available_count,omitempty"` 291} 292 293type StockSendRule struct { 294 MaxCount *int64 `json:"max_count,omitempty"` 295 MaxCountPerDay *int64 `json:"max_count_per_day,omitempty"` 296 MaxCountPerUser *int64 `json:"max_count_per_user,omitempty"` 297} 298 299type StockUsageRule struct { 300 CouponAvailablePeriod *CouponAvailablePeriod `json:"coupon_available_period,omitempty"` 301 NormalCoupon *NormalCouponUsageRule `json:"normal_coupon,omitempty"` 302 DiscountCoupon *DiscountCouponUsageRule `json:"discount_coupon,omitempty"` 303 ExchangeCoupon *ExchangeCouponUsageRule `json:"exchange_coupon,omitempty"` 304} 305 306type StockBundleInfo struct { 307 StockBundleId *string `json:"stock_bundle_id,omitempty"` 308 StockBundleIndex *int64 `json:"stock_bundle_index,omitempty"` 309} 310 311type StockSentCountInfo struct { 312 TotalCount *int64 `json:"total_count,omitempty"` 313 TodayCount *int64 `json:"today_count,omitempty"` 314} 315 316type StockState string 317 318func (e StockState) Ptr() *StockState { 319 return &e 320} 321 322const ( 323 STOCKSTATE_AUDITING StockState = "AUDITING" 324 STOCKSTATE_SENDING StockState = "SENDING" 325 STOCKSTATE_PAUSED StockState = "PAUSED" 326 STOCKSTATE_STOPPED StockState = "STOPPED" 327 STOCKSTATE_DEACTIVATED StockState = "DEACTIVATED" 328) 329 330type CouponAvailablePeriod struct { 331 AvailableBeginTime *string `json:"available_begin_time,omitempty"` 332 AvailableEndTime *string `json:"available_end_time,omitempty"` 333 AvailableDays *int64 `json:"available_days,omitempty"` 334 WaitDaysAfterReceive *int64 `json:"wait_days_after_receive,omitempty"` 335 WeeklyAvailablePeriod *FixedWeekPeriod `json:"weekly_available_period,omitempty"` 336 IrregularAvailablePeriodList []TimePeriod `json:"irregular_available_period_list,omitempty"` 337} 338 339type NormalCouponUsageRule struct { 340 Threshold *int64 `json:"threshold,omitempty"` 341 DiscountAmount *int64 `json:"discount_amount,omitempty"` 342} 343 344type DiscountCouponUsageRule struct { 345 Threshold *int64 `json:"threshold,omitempty"` 346 PercentOff *int64 `json:"percent_off,omitempty"` 347} 348 349type ExchangeCouponUsageRule struct { 350 Threshold *int64 `json:"threshold,omitempty"` 351 ExchangePrice *int64 `json:"exchange_price,omitempty"` 352} 353 354type FixedWeekPeriod struct { 355 DayList []WeekEnum `json:"day_list,omitempty"` 356 DayPeriodList []PeriodOfTheDay `json:"day_period_list,omitempty"` 357} 358 359type TimePeriod struct { 360 BeginTime *string `json:"begin_time,omitempty"` 361 EndTime *string `json:"end_time,omitempty"` 362} 363 364type WeekEnum string 365 366func (e WeekEnum) Ptr() *WeekEnum { 367 return &e 368} 369 370const ( 371 WEEKENUM_MONDAY WeekEnum = "MONDAY" 372 WEEKENUM_TUESDAY WeekEnum = "TUESDAY" 373 WEEKENUM_WEDNESDAY WeekEnum = "WEDNESDAY" 374 WEEKENUM_THURSDAY WeekEnum = "THURSDAY" 375 WEEKENUM_FRIDAY WeekEnum = "FRIDAY" 376 WEEKENUM_SATURDAY WeekEnum = "SATURDAY" 377 WEEKENUM_SUNDAY WeekEnum = "SUNDAY" 378) 379 380type PeriodOfTheDay struct { 381 BeginTime *int64 `json:"begin_time,omitempty"` 382 EndTime *int64 `json:"end_time,omitempty"` 383} 384
应答参数
200 OK
stock_bundle_id 必填 string(40)
【批次组ID】 商品券批次组的唯一标识,由微信支付生成
stock_list 必填 array[object]
【批次列表】 批次组内批次列表
| 属性 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
product_coupon_id 必填 string(40) 【商品券ID】 商品券的唯一标识,由微信支付生成 stock_id 必填 string(40) 【批次ID】 商品券批次的唯一标识,由微信支付生成 remark 选填 string(20) 【备注】 仅配置品牌可见,用于自定义信息 coupon_code_mode 必填 string 【券Code分配模式】 决定发券时用户商品券Code如何产生 可选取值
coupon_code_count_info 选填 object 【品牌方预上传的券Code数量信息】 当且仅当
stock_send_rule 必填 object 【发放规则】 发放规则
progressive_bundle_usage_rule 选填 object 【多次优惠使用规则】 本批次属于某个多次优惠批次组,这里记录的是本批次的使用规则,当且仅当
stock_bundle_info 必填 object 【批次组信息】 批次所在批次组信息
usage_rule_display_info 必填 object 【券使用规则展示信息】 券使用规则展示信息
coupon_display_info 必填 object 【用户商品券展示信息】 用户商品券在卡包中的展示详情,包括引导用户的自定义入口
notify_config 必填 object 【事件通知配置】 发生券相关事件时,微信支付会向品牌方发送通知,需要提供通知相关配置
store_scope 必填 string 【可用门店范围】 控制该批次可以在品牌下哪些门店使用 可选取值
sent_count_info 必填 object 【已发放次数】 本批次已发放次数
state 必填 string 【批次状态】 商品券批次状态 可选取值
deactivate_request_no 选填 string(128) 【失效请求单号】 当且仅当 deactivate_time 选填 string 【失效时间】 当且仅当 deactivate_reason 选填 string(150) 【失效原因】 当且仅当 |
应答示例
200 OK
1{ 2 "stock_bundle_id" : "123456789", 3 "stock_list" : [ 4 { 5 "product_coupon_id" : "200000001", 6 "stock_id" : "123456789", 7 "remark" : "满减券", 8 "coupon_code_mode" : "UPLOAD", 9 "coupon_code_count_info" : { 10 "total_count" : 10000, 11 "available_count" : 999 12 }, 13 "stock_send_rule" : { 14 "max_count" : 10000000, 15 "max_count_per_day" : 10000, 16 "max_count_per_user" : 1 17 }, 18 "progressive_bundle_usage_rule" : { 19 "coupon_available_period" : { 20 "available_begin_time" : "2025-01-01T00:00:00+08:00", 21 "available_end_time" : "2025-10-01T00:00:00+08:00", 22 "available_days" : 10, 23 "wait_days_after_receive" : 1, 24 "weekly_available_period" : { 25 "day_list" : [ 26 "MONDAY" 27 ], 28 "day_period_list" : [ 29 { 30 "begin_time" : 60, 31 "end_time" : 86399 32 } 33 ] 34 }, 35 "irregular_available_period_list" : [ 36 { 37 "begin_time" : "2025-01-01T00:00:00+08:00", 38 "end_time" : "2025-10-01T00:00:00+08:00" 39 } 40 ] 41 }, 42 "normal_coupon" : { 43 "threshold" : 10000, 44 "discount_amount" : 100 45 }, 46 "discount_coupon" : { 47 "threshold" : 10000, 48 "percent_off" : 30 49 }, 50 "exchange_coupon" : { 51 "threshold" : 10000, 52 "exchange_price" : 100 53 } 54 }, 55 "stock_bundle_info" : { 56 "stock_bundle_id" : "123456789", 57 "stock_bundle_index" : 0 58 }, 59 "usage_rule_display_info" : { 60 "coupon_usage_method_list" : [ 61 "MINI_PROGRAM" 62 ], 63 "mini_program_appid" : "wx1234567890", 64 "mini_program_path" : "/pages/index/product", 65 "app_path" : "https://www.example.com/jump-to-app", 66 "usage_description" : "全场可用", 67 "coupon_available_store_info" : { 68 "description" : "可在上海市区的所有门店使用,详细列表参考小程序内信息为准", 69 "mini_program_appid" : "wx1234567890", 70 "mini_program_path" : "/pages/index/store-list" 71 } 72 }, 73 "coupon_display_info" : { 74 "code_display_mode" : "QRCODE", 75 "background_color" : "Color010", 76 "entrance_mini_program" : { 77 "appid" : "wx1234567890", 78 "path" : "/pages/index/product", 79 "entrance_wording" : "欢迎选购", 80 "guidance_wording" : "获取更多优惠" 81 }, 82 "entrance_official_account" : { 83 "appid" : "wx1234567890" 84 }, 85 "entrance_finder" : { 86 "finder_id" : "gh_12345678", 87 "finder_video_id" : "UDFsdf24df34dD456Hdf34", 88 "finder_video_cover_image_url" : "https://wxpaylogo.qpic.cn/wxpaylogo/xxxxx/xxx" 89 } 90 }, 91 "notify_config" : { 92 "notify_appid" : "wx4fd12345678" 93 }, 94 "store_scope" : "SPECIFIC", 95 "sent_count_info" : { 96 "total_count" : 100, 97 "today_count" : 10 98 }, 99 "state" : "SENDING", 100 "deactivate_request_no" : "1002600620019090123143254436", 101 "deactivate_time" : "2025-01-01T00:00+08:00", 102 "deactivate_reason" : "批次信息有误,重新创建" 103 } 104 ] 105} 106
错误码
以下是本接口返回的错误码列表。详细错误码规则,请参考微信支付接口规则-错误码和错误提示


