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

