master #22

Merged
kdletters merged 48 commits from master into release 2026-05-15 11:55:42 +08:00
1649 changed files with 171299 additions and 153409 deletions
+4
View File
@@ -0,0 +1,4 @@
Set-Location 'C:\Genarrative'
$env:RUST_SERVER_TARGET = 'http://127.0.0.1:8082'
$env:GENARRATIVE_RUNTIME_SERVER_TARGET = 'http://127.0.0.1:8082'
npm.cmd run dev:web *> 'C:\Genarrative\.codex\logs\dev-web-final.out.log'
+21 -1
View File
@@ -47,7 +47,7 @@ Default body:
}
```
For a reference image, add:
For weak visual references in text-to-image generation, add:
```json
{
@@ -55,6 +55,26 @@ For a reference image, add:
}
```
For image-to-image work that must follow a reference image closely, use the VectorEngine edits endpoint instead of the generations `image` array:
```text
POST {VECTOR_ENGINE_BASE_URL}/v1/images/edits
Authorization: Bearer {VECTOR_ENGINE_API_KEY}
Content-Type: multipart/form-data
```
Multipart fields:
```text
model=gpt-image-2
prompt=<prompt>
n=1
size=1024x1024
image=@reference.png
```
Prefer edits for workflows where the reference image controls composition, pose, container shape, or layout. In this repository, Match3D container UI generation uses edits with `public/match3d-background-references/pot-fused-reference.png` as the `image` part.
Accept image output from `data[].url`, `data[].b64_json`, or direct nested `url` fields. VectorEngine GPT-image-2-all currently returns synchronously; do not poll APIMart task endpoints.
## Environment
@@ -0,0 +1,351 @@
import { Buffer } from 'node:buffer';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const skillRoot = path.resolve(__dirname, '..');
const repoRoot = path.resolve(skillRoot, '..', '..', '..');
const defaultOutDir = path.join(repoRoot, 'public', 'anthro-cat-illustrations');
const defaultTimeoutMs = 1000000;
const prompts = [
{
id: 'cat-barista',
title: '咖啡师猫咪',
subject:
'一只奶油色猫咪像人一样双足站立,穿深绿色围裙,在温暖咖啡馆吧台前专注拉花,爪子扶着咖啡杯,蓬松尾巴自然弯起,童书级精致插画,柔和自然光,主体清晰。',
},
{
id: 'cat-detective',
title: '侦探猫咪',
subject:
'一只黑白猫咪像侦探一样双足站在雨后街角,穿短风衣和小帽子,单爪拿放大镜,另一只爪插兜,路灯和湿润石板路反光,电影感但可爱,插画风格。',
},
{
id: 'cat-dancer',
title: '舞者猫咪',
subject:
'一只橘猫以拟人舞者姿态单脚旋转,穿轻盈舞台披肩,前爪展开,尾巴形成优雅弧线,背景是暖色小剧场灯光,动作灵动,精致插画。',
},
{
id: 'cat-knight',
title: '骑士猫咪',
subject:
'一只银灰猫咪像小骑士一样站在苔藓石台上,披短斗篷,双爪握着细剑指向地面,姿态勇敢但可亲,远处森林微光,奇幻插画风格。',
},
{
id: 'cat-painter',
title: '画家猫咪',
subject:
'一只三花猫咪双足站在画架前,穿宽松蓝色工作衫,一爪拿画笔一爪托调色盘,鼻尖有颜料点,窗边画室阳光明亮,温柔手绘插画。',
},
{
id: 'cat-astronaut',
title: '宇航员猫咪',
subject:
'一只白猫咪以拟人宇航员姿态站在月面,透明头盔内露出猫脸,尾巴在宇航服后轻轻翘起,爪子向远处蓝色星球敬礼,梦幻插画风格。',
},
];
const args = new Map();
for (let index = 2; index < process.argv.length; index += 1) {
const raw = process.argv[index];
if (raw.startsWith('--')) {
const next = process.argv[index + 1];
if (next && !next.startsWith('--')) {
args.set(raw, next);
index += 1;
} else {
args.set(raw, true);
}
}
}
function readDotenv(fileName) {
const filePath = path.join(repoRoot, fileName);
if (!existsSync(filePath)) {
return {};
}
const values = {};
for (const line of readFileSync(filePath, 'utf8').split(/\r?\n/u)) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) {
continue;
}
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/u.exec(trimmed);
if (!match) {
continue;
}
let value = match[2].trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
values[match[1]] = value;
}
return values;
}
function resolveEnv() {
const loaded = {
...readDotenv('.env.example'),
...readDotenv('.env.local'),
...readDotenv('.env.secrets.local'),
...process.env,
};
return {
baseUrl: String(loaded.VECTOR_ENGINE_BASE_URL || '')
.trim()
.replace(/\/+$/u, ''),
apiKey: String(loaded.VECTOR_ENGINE_API_KEY || '').trim(),
timeoutMs: Number.parseInt(
String(loaded.VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS || defaultTimeoutMs),
10,
),
};
}
function buildVectorEngineImagesGenerationUrl(baseUrl) {
return baseUrl.endsWith('/v1')
? `${baseUrl}/images/generations`
: `${baseUrl}/v1/images/generations`;
}
function buildPrompt(entry) {
return [
'请生成一张高清 1:1 方形插画。',
`画面主体:${entry.subject}`,
'要求:猫咪保留清晰猫脸、猫耳、猫尾和毛发质感,但身体姿态像人一样自然;构图完整,角色占画面主体,适合作为项目插画素材。',
'避免:文字、水印、边框、按钮、UI 元素、低清晰度、过度写实恐怖感、畸形肢体、多余手指。',
].join('');
}
function collectStringsByKey(value, targetKey, output) {
if (Array.isArray(value)) {
value.forEach((entry) => collectStringsByKey(entry, targetKey, output));
return;
}
if (!value || typeof value !== 'object') {
return;
}
for (const [key, nested] of Object.entries(value)) {
if (key === targetKey) {
if (typeof nested === 'string' && nested.trim()) {
output.push(nested.trim());
}
if (Array.isArray(nested)) {
nested.forEach((entry) => {
if (typeof entry === 'string' && entry.trim()) {
output.push(entry.trim());
}
});
}
}
collectStringsByKey(nested, targetKey, output);
}
}
function extractImageUrls(payload) {
const urls = [];
collectStringsByKey(payload, 'url', urls);
collectStringsByKey(payload, 'image', urls);
collectStringsByKey(payload, 'image_url', urls);
return [...new Set(urls)].filter((url) => /^https?:\/\//u.test(url));
}
function extractBase64Images(payload) {
const values = [];
collectStringsByKey(payload, 'b64_json', values);
return values;
}
function inferExtensionFromContentType(contentType) {
const normalized = contentType.split(';')[0]?.trim().toLowerCase();
if (normalized === 'image/png') {
return 'png';
}
if (normalized === 'image/webp') {
return 'webp';
}
if (normalized === 'image/gif') {
return 'gif';
}
return 'jpg';
}
function inferExtensionFromBytes(bytes) {
if (bytes.subarray(0, 8).equals(Buffer.from('\x89PNG\r\n\x1A\n', 'binary'))) {
return 'png';
}
if (bytes.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff]))) {
return 'jpg';
}
if (
bytes.subarray(0, 4).toString('ascii') === 'RIFF' &&
bytes.subarray(8, 12).toString('ascii') === 'WEBP'
) {
return 'webp';
}
return 'png';
}
async function fetchJson(url, options, timeoutMs) {
const abortController = new AbortController();
const timer = setTimeout(() => abortController.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: abortController.signal,
});
const text = await response.text();
if (!response.ok) {
throw new Error(`VectorEngine ${response.status}: ${text.slice(0, 600)}`);
}
return JSON.parse(text);
} catch (error) {
if (error?.name === 'AbortError') {
throw new Error(`VectorEngine request timed out after ${timeoutMs}ms`);
}
throw error;
} finally {
clearTimeout(timer);
}
}
async function downloadUrl(url, timeoutMs) {
const abortController = new AbortController();
const timer = setTimeout(() => abortController.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: abortController.signal });
if (!response.ok) {
throw new Error(`download ${response.status}`);
}
const bytes = Buffer.from(await response.arrayBuffer());
return {
bytes,
extension: inferExtensionFromContentType(
response.headers.get('content-type') || 'image/jpeg',
),
};
} catch (error) {
if (error?.name === 'AbortError') {
throw new Error(`Generated image download timed out after ${timeoutMs}ms`);
}
throw error;
} finally {
clearTimeout(timer);
}
}
async function generateOne(env, entry, outDir) {
const requestBody = {
model: 'gpt-image-2-all',
prompt: buildPrompt(entry),
n: 1,
size: '1024x1024',
};
const payload = await fetchJson(
buildVectorEngineImagesGenerationUrl(env.baseUrl),
{
method: 'POST',
headers: {
Authorization: `Bearer ${env.apiKey}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
},
env.timeoutMs,
);
const urls = extractImageUrls(payload);
const b64Images = extractBase64Images(payload);
let image;
if (urls[0]) {
image = await downloadUrl(urls[0], env.timeoutMs);
} else if (b64Images[0]) {
const bytes = Buffer.from(b64Images[0], 'base64');
image = {
bytes,
extension: inferExtensionFromBytes(bytes),
};
} else {
throw new Error(`VectorEngine returned no image for ${entry.id}`);
}
mkdirSync(outDir, { recursive: true });
const outputPath = path.join(outDir, `${entry.id}.${image.extension}`);
writeFileSync(outputPath, image.bytes);
return outputPath;
}
const dryRun = args.has('--dry-run') || !args.has('--live');
const outDir = path.resolve(String(args.get('--out-dir') || defaultOutDir));
const limit = Number.parseInt(String(args.get('--limit') || '0'), 10);
const selectedPrompts = limit > 0 ? prompts.slice(0, limit) : prompts;
if (dryRun) {
const env = resolveEnv();
console.log(
JSON.stringify(
{
mode: 'dry-run',
outDir,
count: selectedPrompts.length,
hasBaseUrl: Boolean(env.baseUrl),
hasApiKey: Boolean(env.apiKey),
requests: selectedPrompts.map((entry) => ({
id: entry.id,
title: entry.title,
body: {
model: 'gpt-image-2-all',
prompt: buildPrompt(entry),
n: 1,
size: '1024x1024',
},
})),
},
null,
2,
),
);
process.exit(0);
}
const env = resolveEnv();
if (!env.baseUrl || !env.apiKey) {
console.error(
JSON.stringify({
ok: false,
error: 'Missing VECTOR_ENGINE_BASE_URL or VECTOR_ENGINE_API_KEY',
hasBaseUrl: Boolean(env.baseUrl),
hasApiKey: Boolean(env.apiKey),
}),
);
process.exit(1);
}
const generated = [];
for (const entry of selectedPrompts) {
console.log(`Generating ${entry.id}...`);
generated.push(await generateOne(env, entry, outDir));
}
console.log(
JSON.stringify(
{
ok: true,
count: generated.length,
files: generated,
},
null,
2,
),
);
@@ -13,7 +13,7 @@ const promptsPath = path.join(
'puzzle-template-prompts.json',
);
const defaultOutDir = path.join(repoRoot, 'public', 'puzzle-creation-templates');
const defaultTimeoutMs = 180000;
const defaultTimeoutMs = 1000000;
const args = new Map();
for (let index = 2; index < process.argv.length; index += 1) {
@@ -0,0 +1,121 @@
---
name: wechatpay-basic-payment
description: 微信支付基础支付解决方案,涵盖支付、退款账单、分账、商户进件、开户意愿确认,提供选型/代码示例/业务速查/质量评估/排障五大能力。Use when user mentions "JSAPI支付", "APP支付", "H5支付", "Native支付", "小程序支付", "付款码支付", "合单支付", "特约商户进件", "开户意愿确认", or asks to "推荐支付方式", "要支付接口代码示例", "排查支付或退款问题".
author: wechatpay
version: "1.0"
---
# 微信支付基础支付 & 合单支付接入指引
## 全局交互规范
> ‼️ 以下规则适用于本技能所有能力、所有对话轮次,优先级高于各能力的局部规则。
1. **所有问题必须得到用户明确回答后才能继续。** 如果一次提出了多个问题,必须逐一检查每个问题是否都已获得用户的明确答复。对于未回答的问题,必须再次追问,**严禁对未回答的问题自行假设、推断或使用默认值**。
2. **接入模式前置确认**:任何能力使用前须先确认**商户模式**或**服务商模式**,已明确则无需重复。两种模式的核心差异见 → [📄 接入模式说明.md](./references/3-商户与服务商通用/接入指南/接入模式说明.md)。
3. **分步确认协议**(简单知识问答除外,需要帮用户排查、分析或执行操作时必须遵守):
- **① 明确需求**:先理解用户问题,给出初步判断或原因分析,不要一上来就堆参数清单。
- **② 征得同意**:主动提出下一步能做什么,**等用户明确同意后**才继续,严禁用户没表态就开始收集参数或执行操作。
- **③ 收集信息**:用户同意后再告知需要哪些信息并逐项收集,收齐才能执行。
- **④ 执行前确认**:准备执行操作前,简要说明即将做什么,确认用户同意后再执行;涉及线上环境须额外提示风险。
## 能力概览
1. **产品选型** — 根据场景推荐支付方式(JSAPI/APP/H5/Native/小程序/付款码),判断是否需要合单支付
2. **示例代码** — 各接口的下单、调起、回调、退款、账单等代码结构示例(只展示不写入)
3. **业务知识速查** — 订单状态、退款规则、账单对账、APPID绑定、特约商户进件、开户意愿确认等
4. **接入质量评估** — 签名验签、业务逻辑完整性、回调处理规范性检查(含合单/分账/进件/开户意愿确认专项)
5. **问题排查** — 下单失败、调起异常、回调收不到、退款失败等(含合单支付专项常见问题)
> 未明确支付方式时先通过能力1引导选型。退款和账单无需确认支付方式,但仍需确认接入模式。合单支付需先确认是否涉及多商户/多APPID场景。特约商户进件和商户开户意愿确认仅适用于服务商/渠道商模式。
## 能力1:产品选型
> 用户问「该用哪种支付方式」或比较各方式区别时 → 加载 `支付产品对比.md`,确定支付方式后再按需加载示例代码。
- 产品对比 + 选型决策树 + 准入条件 + 调起支付差异 → [📄 支付产品对比.md](./references/3-商户与服务商通用/产品选型/支付产品对比.md)
## 能力2:示例代码
> 用户要某个接口的代码示例时 → 确认接入模式和语言,加载对应模式的 `接口索引.md` 定位代码文件。
>
> ‼️ **只检索、不生成。** 严禁从零编写任何代码,必须从代码示例文件中检索获取。
>
> ‼️ **只展示、不写入。** 代码示例仅用于讲解 API 调用结构和签名流程,严禁直接写入用户项目(禁止调用 write_to_file、replace_in_file 等工具创建或修改项目文件)。在对话中展示代码,让用户自行复制适配。
>
> ‼️ **先交互、后输出。** 提供代码前必须先确认接入模式、开发语言和具体接口,每次只输出一个接口;提供完代码后主动推荐接入质量评估。
>
> ‼️ **支付方式仅「下单」和「调起支付」接口需确认,其他接口无需询问支付方式。** 用户请求查单、关单、退款、回调处理、账单等通用接口时,只需确认接入模式和开发语言,无需询问支付方式——这些接口各支付方式完全相同。**但合单支付的查单、关单、回调使用专用接口,需确认用户是基础支付还是合单支付。**
>
> ‼️ **用户语言非 Java/Go 时**(本 skill 仅维护 Java/Go 示例):**禁止**直接生成跨语言代码。流程:
> 1. 用 `AskQuestion` 获明确同意(文案需明示「参考实现 / 非官方维护 / 须自行 review 与测试」),未同意只发官方 Java/Go 原文。
> 2. 同意后以官方 Java 示例为基准翻译生成业务代码「参考实现」;再用纯文字问是否翻 Java 公库(SDK 工具类 + HTTP 客户端),未明确要不贴。每段代码前附下方免责块。
>
> > ⚠️ 以下代码为**跨语言参考实现**,由 AI 参考官方 Java 示例翻译生成,并非微信支付官方维护。
> > - 请**逐行 review** 签名构造、HTTP 调用、字段命名、回调解密等关键逻辑。
> > - 上线前必须在测试环境完整验证,建议先以官方 Java/Go 示例打通主链路作为对照。
> > - 出现接入问题时以官方 Java/Go 示例为准。
- 涉及提供示例代码时,按接入模式查阅对应接口索引,定位目标代码文件:
- 商户模式 → [📄 接口索引.md](./references/1-商户/示例代码/接口索引.md)
- 服务商模式 → [📄 接口索引.md](./references/2-服务商/示例代码/接口索引.md)
> **加载策略**:先确认接入模式,读对应的 `接口索引.md` 定位用户需要的接口对应的文件路径,再按需加载具体文件。不要一次性加载所有文件。
## 能力3:业务知识速查
> 用户问参数获取、APPID绑定、订单状态、退款规则、分账等业务知识时 → 按接入模式加载对应文档。
- 开发必要参数 / APPID类型 / APPID绑定流程:
- 商户模式 → [📄 开发必要参数说明.md](./references/1-商户/接入指南/开发必要参数说明.md)
- 服务商模式 → [📄 开发必要参数说明.md](./references/2-服务商/接入指南/开发必要参数说明.md)
- 点金计划(服务商 JSAPI 必接) → [📄 点金计划.md](./references/2-服务商/接入指南/点金计划.md)
- 订单状态 / 关单 / 终态 → [📄 订单状态流转.md](./references/3-商户与服务商通用/接入指南/订单状态流转.md)
- 分账 → [📄 分账接入指南.md](./references/3-商户与服务商通用/接入指南/分账接入指南.md)
- 特约商户进件(仅服务商) → [📄 特约商户进件.md](./references/2-服务商/接入指南/特约商户进件.md)
- 商户开户意愿确认(仅服务商/渠道商) → [📄 商户开户意愿确认.md](./references/2-服务商/接入指南/商户开户意愿确认.md)
- 退款规则 / 账单对账 → 已整合到示例代码注释中,通过能力2加载
> **加载策略**:按关键词匹配文档,区分接入模式。特约商户进件和商户开户意愿确认为服务商/渠道商专属,商户模式无需加载。
## 能力4:接入质量评估
> 用户准备上线或想检查代码隐患时 → 加载以下文档。
>
> ‼️ **只检查用户实际使用的功能模块。** 合单支付、分账、进件、开户意愿确认等模块须先确认用户是否涉及,**未使用的不检查、不提及**。
- 签名验签 → [📄 签名与验签规则.md](./references/3-商户与服务商通用/接入指南/签名与验签规则.md)
- 业务逻辑完整性(含质检人设 + 检查清单) → [📄 接入质量检查清单.md](./references/3-商户与服务商通用/接入指南/接入质量检查清单.md)
- 回调处理规范 → [📄 回调通知处理.md](./references/3-商户与服务商通用/接入指南/回调通知处理.md)
## 能力5:问题排查
> 用户遇到报错或接口调用异常时 → 按下方路径分流加载。
>
> ‼️ **排障推荐示例代码时,必须先确认开发语言,只推荐对应的示例。** 排障手册中每个错误码的「示例代码推荐」可能涉及 Java/Go 两种语言示例,但输出时**只输出匹配的示例**。开发语言尚未确认时,先在推荐示例代码时自然地询问用户。
>
> ‼️ **用户语言非 Java/Go 时按能力 2 的跨语言确认流程处理**(弹框确认 → 参考生成 + 免责块 + 公库分步)。先用文字说明 Java/Go 示例中的关键修复点(签名、字段、流程),再走完整流程后再生成对应语言的"参考修复代码"。
- 排障手册(错误码 TOP 20 速查 + 定位流程 + 服务商特有问题)→ [📄 排障手册.md](./references/3-商户与服务商通用/问题排查/排障手册.md)
- 基础支付常见问题 → [📄 基础支付常见问题.md](./references/3-商户与服务商通用/问题排查/基础支付常见问题.md)
- 分账常见问题 → [📄 分账常见问题.md](./references/3-商户与服务商通用/问题排查/分账常见问题.md)
- 合单支付常见问题 → [📄 合单支付常见问题.md](./references/3-商户与服务商通用/问题排查/合单支付常见问题.md)
- 排障辅助脚本(排障手册中 🔧 标注的场景):`scripts/商户/``scripts/服务商/` 下各有 `查询订单.py``查询退款.py`
> **加载策略**
>
> - **路径A(有 Request-Id**→ 读 `排障手册.md`,提取错误码匹配 TOP 20 速查表直接给出方案;标注 🔧 的引导用户执行脚本。未命中则按手册各章节排查,仍未解决再加载对应常见问题文档兜底。
> - **路径B(无 Request-Id)**→ 确认支付方式,加载对应常见问题文档匹配。未命中再加载 `排障手册.md` 兜底。
> - **路径C(进件/开户意愿确认)**→ 直接加载 `特约商户进件.md` 或 `商户开户意愿确认.md`,文档末尾的常见问题和常见报错覆盖高频问题。
>
> **脚本使用规范**:脚本采用签名模式,不获取用户私钥。引导用户在自己服务器完成签名后,将签名值(Base64)、时间戳、随机串传入脚本。执行前需按分步确认协议征得同意。
---
> 以下信息与技能能力无关,仅供查阅。
## 💬 社区与反馈
在使用过程中遇到问题、有改进建议,或者想和其他开发者交流接入经验,欢迎扫码添加企业微信进群,与官方团队和社区开发者一起讨论:
![微信支付 Skills 交流群二维码](https://raw.githubusercontent.com/wechatpay-apiv3/wechatpay-skills/main/wechatpay-basic-payment/assets/qrcode.jpg)
Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

@@ -0,0 +1,83 @@
# 开发必要参数说明
普通商户模式接入微信支付 APIv3 前,需要准备开发必要参数(mchid、appid、商户API证书、微信支付公钥、APIv3密钥等)。
## APPID 详解与绑定
### APPID 类型
APPID 是微信生态中应用的唯一标识,格式都是 `wx` + 一串字符(如 `wxd678efh567hg6787`),根据注册平台不同分为三种类型:
| APPID 类型 | 注册平台 | 用途 |
|-----------|---------|------|
| 公众号 AppID | 公众平台(mp.weixin.qq.com) | 服务号/订阅号,用于公众号内网页场景 |
| 小程序 AppID | 公众平台(mp.weixin.qq.com | 微信小程序场景 |
| 移动应用 AppID | 开放平台(open.weixin.qq.com | 原生 APPiOS/Android/鸿蒙)场景 |
> **三种 APPID 格式相同但不能混用**。拿小程序 AppID 做 JSAPI 支付会报错,拿公众号 AppID 做 APP 支付也不行。
### 为什么需要绑定 APPID
微信支付的所有支付方式都要求商户号与 APPID 建立绑定关系,未绑定时下单接口会报错。
### 如何查询 APPID
| APPID 类型 | 查询路径 |
|-----------|---------|
| 服务号/公众号 | 登录公众平台 → 设置与开发 → 开发接口管理 → 基本配置 → 开发者ID(AppID) |
| 小程序 | 登录公众平台 → 开发与服务 → 开发管理 → 开发设置 → AppID(小程序ID) |
| 移动应用 | 登录开放平台 → 管理中心 → 移动应用 → 查看 → 详情页面 → APPID |
### 如何绑定
**第一步:在商户平台发起绑定申请**
登录商户平台 → 产品中心 → APPID授权管理 → +关联AppID → 新增授权 → 填写 APPID → 提交
- 主体一致:直接填写 APPID 提交
- 主体不一致:还需填写 APPID 认证主体,并勾选《微信支付联合营运承诺函》
**第二步:在对应平台确认授权**
| APPID 类型 | 确认路径 |
|-----------|---------|
| 服务号/公众号 | 登录公众平台 → 微信支付 → 商户号管理 → 待关联商户号 → 确认 |
| 小程序 | 登录公众平台 → 微信支付 → 商户号管理 → 待关联商户号 → 确认 |
| 移动应用 | 登录开放平台 → 移动应用 → 详情 → 能力专区 → 微信支付 → 查询详情 → 待关联商户号 → 确认 |
> 委托第三方创建的小程序,需先设置邮箱密码后登录 PC 端确认。
**第三步:查看绑定结果**
登录商户平台 → 产品中心 → APPID账号管理 → 我关联的APPID账号
### 绑定限制
| 限制项 | 说明 |
|-------|------|
| 数量上限 | 一个商户号最多关联 50 个 APPID |
| 解绑 | 绑定后不支持解绑,每条关系相互独立 |
| 跨主体 | 需补充 APPID 主体信息 |
| 特殊费率 | 享有特殊行业费率的商户号,提交后有额外审核(1-3个工作日) |
| 费率一致性 | APPID 已绑定其他商户号时,新商户号的费率需与已绑定的一致 |
| 风控 | 商户号或 APPID 存在风险时(资料不全、有未处理处罚等),可能增加审核或被驳回 |
### APPID 相关常见报错
| 报错信息 | 原因 | 处理方式 |
|---------|------|---------|
| `appid and mchid not match` | 下单时传入的 appid 与商户号未建立绑定关系 | 按上述流程绑定 |
| `appid is invalid` | appid 格式不对,或使用了错误类型的 appid | 检查是否用了正确类型的 APPID(如 JSAPI 需要公众号 AppID,不能用小程序 AppID |
| JSAPI 支付报权限错误 | 商户号绑定的是小程序 APPID,但用 JSAPI 调起 | JSAPI 需要绑定服务号 APPID |
## 参数与代码示例的对应关系
示例代码中构造函数所需的参数与上述开发必要参数的对应:
```
mchid → 商户号
certificateSerialNo → 商户API证书序列号
privateKeyFilePath → 商户API证书私钥文件路径(apiclient_key.pem
wechatPayPublicKeyId → 微信支付公钥ID
wechatPayPublicKeyFilePath → 微信支付公钥文件路径(wxp_pub.pem
```
@@ -0,0 +1,203 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &DirectApiv3JsapiPrepayRequest{
Appid: wxpay_utility.String("wxd678efh567hg6787"),
Mchid: wxpay_utility.String("1230000109"),
Description: wxpay_utility.String("Image形象店-深圳腾大-QQ公仔"),
OutTradeNo: wxpay_utility.String("1217752501201407033233368018"),
TimeExpire: wxpay_utility.Time(time.Now()),
Attach: wxpay_utility.String("自定义数据说明"),
NotifyUrl: wxpay_utility.String(" https://www.weixin.qq.com/wxpay/pay.php"),
GoodsTag: wxpay_utility.String("WXG"),
SupportFapiao: wxpay_utility.Bool(false),
Amount: &CommonAmountInfo{
Total: wxpay_utility.Int64(100),
Currency: wxpay_utility.String("CNY"),
},
Payer: &JsapiReqPayerInfo{
Openid: wxpay_utility.String("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"),
},
Detail: &CouponInfo{
CostPrice: wxpay_utility.Int64(608800),
InvoiceId: wxpay_utility.String("微信123"),
GoodsDetail: []GoodsDetail{GoodsDetail{
MerchantGoodsId: wxpay_utility.String("1246464644"),
WechatpayGoodsId: wxpay_utility.String("1001"),
GoodsName: wxpay_utility.String("iPhoneX 256G"),
Quantity: wxpay_utility.Int64(1),
UnitPrice: wxpay_utility.Int64(528800),
}},
},
SceneInfo: &CommonSceneInfo{
PayerClientIp: wxpay_utility.String("14.23.150.211"),
DeviceId: wxpay_utility.String("013467007045764"),
StoreInfo: &StoreInfo{
Id: wxpay_utility.String("0001"),
Name: wxpay_utility.String("腾讯大厦分店"),
AreaCode: wxpay_utility.String("440305"),
Address: wxpay_utility.String("广东省深圳市南山区科技中一道10000号"),
},
},
SettleInfo: &SettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
}
response, err := JsapiPrepay(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func JsapiPrepay(config *wxpay_utility.MchConfig, request *DirectApiv3JsapiPrepayRequest) (response *DirectApiv3JsapiPrepayResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/pay/transactions/jsapi"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &DirectApiv3JsapiPrepayResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type DirectApiv3JsapiPrepayRequest struct {
Appid *string `json:"appid,omitempty"`
Mchid *string `json:"mchid,omitempty"`
Description *string `json:"description,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
TimeExpire *time.Time `json:"time_expire,omitempty"`
Attach *string `json:"attach,omitempty"`
NotifyUrl *string `json:"notify_url,omitempty"`
GoodsTag *string `json:"goods_tag,omitempty"`
SupportFapiao *bool `json:"support_fapiao,omitempty"`
Amount *CommonAmountInfo `json:"amount,omitempty"`
Payer *JsapiReqPayerInfo `json:"payer,omitempty"`
Detail *CouponInfo `json:"detail,omitempty"`
SceneInfo *CommonSceneInfo `json:"scene_info,omitempty"`
SettleInfo *SettleInfo `json:"settle_info,omitempty"`
}
type DirectApiv3JsapiPrepayResponse struct {
PrepayId *string `json:"prepay_id,omitempty"`
}
type CommonAmountInfo struct {
Total *int64 `json:"total,omitempty"`
Currency *string `json:"currency,omitempty"`
}
type JsapiReqPayerInfo struct {
Openid *string `json:"openid,omitempty"`
}
type CouponInfo struct {
CostPrice *int64 `json:"cost_price,omitempty"`
InvoiceId *string `json:"invoice_id,omitempty"`
GoodsDetail []GoodsDetail `json:"goods_detail,omitempty"`
}
type CommonSceneInfo struct {
PayerClientIp *string `json:"payer_client_ip,omitempty"`
DeviceId *string `json:"device_id,omitempty"`
StoreInfo *StoreInfo `json:"store_info,omitempty"`
}
type SettleInfo struct {
ProfitSharing *bool `json:"profit_sharing,omitempty"`
}
type GoodsDetail struct {
MerchantGoodsId *string `json:"merchant_goods_id,omitempty"`
WechatpayGoodsId *string `json:"wechatpay_goods_id,omitempty"`
GoodsName *string `json:"goods_name,omitempty"`
Quantity *int64 `json:"quantity,omitempty"`
UnitPrice *int64 `json:"unit_price,omitempty"`
}
type StoreInfo struct {
Id *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
AreaCode *string `json:"area_code,omitempty"`
Address *string `json:"address,omitempty"`
}
@@ -0,0 +1,177 @@
package main
import (
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
)
// 申请交易账单API
//
// 关键注意:
// 1. 次日10点后拉取,API仅支持3个月内单日账单,更早的需在商户平台下载。
// 2. 返回的是下载链接(download_url),需二次请求下载(gzip压缩CSV)。
// 3. 账单金额单位为"元",与下单API的"分"不同,对账时注意转换。
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &GetTradeBillRequest{
BillDate: wxpay_utility.String("2019-06-11"),
BillType: BILLTYPE_ALL.Ptr(),
TarType: TARTYPE_GZIP.Ptr(),
}
response, err := GetTradeBill(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
// GetTradeBill 申请交易账单API
func GetTradeBill(config *wxpay_utility.MchConfig, request *GetTradeBillRequest) (response *QueryBillEntity, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "GET"
path = "/v3/bill/tradebill"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
query := reqUrl.Query()
if request.BillDate != nil {
query.Add("bill_date", *request.BillDate)
}
if request.BillType != nil {
query.Add("bill_type", fmt.Sprintf("%v", *request.BillType))
}
if request.TarType != nil {
query.Add("tar_type", fmt.Sprintf("%v", *request.TarType))
}
reqUrl.RawQuery = query.Encode()
httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &QueryBillEntity{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type GetTradeBillRequest struct {
BillDate *string `json:"bill_date,omitempty"`
BillType *BillType `json:"bill_type,omitempty"`
TarType *TarType `json:"tar_type,omitempty"`
}
func (o *GetTradeBillRequest) MarshalJSON() ([]byte, error) {
type Alias GetTradeBillRequest
a := &struct {
BillDate *string `json:"bill_date,omitempty"`
BillType *BillType `json:"bill_type,omitempty"`
TarType *TarType `json:"tar_type,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
BillDate: nil,
BillType: nil,
TarType: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type QueryBillEntity struct {
HashType *HashType `json:"hash_type,omitempty"`
HashValue *string `json:"hash_value,omitempty"`
DownloadUrl *string `json:"download_url,omitempty"`
}
type BillType string
func (e BillType) Ptr() *BillType {
return &e
}
const (
BILLTYPE_ALL BillType = "ALL"
BILLTYPE_SUCCESS BillType = "SUCCESS"
BILLTYPE_REFUND BillType = "REFUND"
)
type TarType string
func (e TarType) Ptr() *TarType {
return &e
}
const (
TARTYPE_GZIP TarType = "GZIP"
)
type HashType string
func (e HashType) Ptr() *HashType {
return &e
}
const (
HASHTYPE_SHA1 HashType = "SHA1"
)
@@ -0,0 +1,171 @@
package main
import (
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &GetFundFlowBillRequest{
BillDate: wxpay_utility.String("2019-06-11"),
AccountType: FUNDFLOWBILLACCOUNTTYPE_BASIC.Ptr(),
TarType: TARTYPE_GZIP.Ptr(),
}
response, err := GetFundFlowBill(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
// GetFundFlowBill 申请资金账单API
func GetFundFlowBill(config *wxpay_utility.MchConfig, request *GetFundFlowBillRequest) (response *QueryBillEntity, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "GET"
path = "/v3/bill/fundflowbill"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
query := reqUrl.Query()
if request.BillDate != nil {
query.Add("bill_date", *request.BillDate)
}
if request.AccountType != nil {
query.Add("account_type", fmt.Sprintf("%v", *request.AccountType))
}
if request.TarType != nil {
query.Add("tar_type", fmt.Sprintf("%v", *request.TarType))
}
reqUrl.RawQuery = query.Encode()
httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &QueryBillEntity{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type GetFundFlowBillRequest struct {
BillDate *string `json:"bill_date,omitempty"`
AccountType *FundFlowBillAccountType `json:"account_type,omitempty"`
TarType *TarType `json:"tar_type,omitempty"`
}
func (o *GetFundFlowBillRequest) MarshalJSON() ([]byte, error) {
type Alias GetFundFlowBillRequest
a := &struct {
BillDate *string `json:"bill_date,omitempty"`
AccountType *FundFlowBillAccountType `json:"account_type,omitempty"`
TarType *TarType `json:"tar_type,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
BillDate: nil,
AccountType: nil,
TarType: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type QueryBillEntity struct {
HashType *HashType `json:"hash_type,omitempty"`
HashValue *string `json:"hash_value,omitempty"`
DownloadUrl *string `json:"download_url,omitempty"`
}
type FundFlowBillAccountType string
func (e FundFlowBillAccountType) Ptr() *FundFlowBillAccountType {
return &e
}
const (
FUNDFLOWBILLACCOUNTTYPE_BASIC FundFlowBillAccountType = "BASIC"
FUNDFLOWBILLACCOUNTTYPE_OPERATION FundFlowBillAccountType = "OPERATION"
FUNDFLOWBILLACCOUNTTYPE_FEES FundFlowBillAccountType = "FEES"
)
type TarType string
func (e TarType) Ptr() *TarType {
return &e
}
const (
TARTYPE_GZIP TarType = "GZIP"
)
type HashType string
func (e HashType) Ptr() *HashType {
return &e
}
const (
HASHTYPE_SHA1 HashType = "SHA1"
)
@@ -0,0 +1,71 @@
package wxpay_utility
import (
"bytes"
"io"
"net/http"
)
const Host = "https://api.mch.weixin.qq.com"
// SendGet 发送 GET 请求并返回已验签的应答 Body
func SendGet(config *MchConfig, uri string) ([]byte, error) {
return sendRequest(config, "GET", uri, nil)
}
// SendPost 发送 POST 请求并返回已验签的应答 Body
func SendPost(config *MchConfig, uri string, reqBody []byte) ([]byte, error) {
return sendRequest(config, "POST", uri, reqBody)
}
func sendRequest(config *MchConfig, method string, uri string, reqBody []byte) ([]byte, error) {
var bodyReader io.Reader
if reqBody != nil {
bodyReader = bytes.NewReader(reqBody)
}
httpRequest, err := http.NewRequest(method, Host+uri, bodyReader)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := BuildAuthorization(config.MchId(), config.CertificateSerialNo(),
config.PrivateKey(), method, uri, reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
if reqBody != nil {
httpRequest.Header.Set("Content-Type", "application/json")
}
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
err = ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
return respBody, nil
}
return nil, NewApiException(httpResponse.StatusCode, httpResponse.Header, respBody)
}
@@ -0,0 +1,191 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &UnionApiv3AppPrepayRequest{
CombineAppid: wxpay_utility.String("wxd678efh567hg6787"),
CombineOutTradeNo: wxpay_utility.String("20150806125345"),
CombineMchid: wxpay_utility.String("1900000109"),
SceneInfo: &UnionSceneInfo{
DeviceId: wxpay_utility.String("POS1:1"),
PayerClientIp: wxpay_utility.String("14.17.22.32"),
},
SubOrders: []UnionSubOrder{
UnionSubOrder{
Mchid: wxpay_utility.String("1230000109"),
OutTradeNo: wxpay_utility.String("20150806125346"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("深圳分店"),
Description: wxpay_utility.String("腾讯充值中心-QQ会员充值"),
Detail: wxpay_utility.String("买单费用"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
UnionSubOrder{
Mchid: wxpay_utility.String("1230000119"),
OutTradeNo: wxpay_utility.String("20150806125347"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("广州分店"),
Description: wxpay_utility.String("腾讯充值中心-微信充值"),
Detail: wxpay_utility.String("买单费用"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
},
CombinePayerInfo: &UnionAppPayerInfo{
Openid: wxpay_utility.String("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"),
},
TimeExpire: wxpay_utility.Time(time.Now()),
NotifyUrl: wxpay_utility.String("https://yourapp.com/notify"),
}
response, err := UnionAppPrepay(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func UnionAppPrepay(config *wxpay_utility.MchConfig, request *UnionApiv3AppPrepayRequest) (response *UnionApiv3AppPrepayResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/combine-transactions/app"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &UnionApiv3AppPrepayResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type UnionApiv3AppPrepayRequest struct {
CombineAppid *string `json:"combine_appid,omitempty"`
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
CombineMchid *string `json:"combine_mchid,omitempty"`
SceneInfo *UnionSceneInfo `json:"scene_info,omitempty"`
SubOrders []UnionSubOrder `json:"sub_orders,omitempty"`
CombinePayerInfo *UnionAppPayerInfo `json:"combine_payer_info,omitempty"`
TimeExpire *time.Time `json:"time_expire,omitempty"`
NotifyUrl *string `json:"notify_url,omitempty"`
TradeScenario *string `json:"trade_scenario,omitempty"`
}
type UnionApiv3AppPrepayResponse struct {
PrepayId *string `json:"prepay_id,omitempty"`
}
type UnionSceneInfo struct {
DeviceId *string `json:"device_id,omitempty"`
PayerClientIp *string `json:"payer_client_ip,omitempty"`
}
type UnionSubOrder struct {
Mchid *string `json:"mchid,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
Amount *UnionAmountInfo `json:"amount,omitempty"`
Attach *string `json:"attach,omitempty"`
Description *string `json:"description,omitempty"`
Detail *string `json:"detail,omitempty"`
GoodsTag *string `json:"goods_tag,omitempty"`
SettleInfo *UnionSettleInfo `json:"settle_info,omitempty"`
}
type UnionAppPayerInfo struct {
Openid *string `json:"openid,omitempty"`
}
type UnionAmountInfo struct {
TotalAmount *int64 `json:"total_amount,omitempty"`
Currency *string `json:"currency,omitempty"`
}
type UnionSettleInfo struct {
ProfitSharing *bool `json:"profit_sharing,omitempty"`
}
@@ -0,0 +1,128 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &UnionCloseRequest{
CombineOutTradeNo: wxpay_utility.String("1217752501201407033233368018"),
CombineAppid: wxpay_utility.String("wxd678efh567hg6787"),
SubOrders: []UnionCloseSubOrder{UnionCloseSubOrder{
Mchid: wxpay_utility.String("1900000109"),
OutTradeNo: wxpay_utility.String("20150806125346"),
}},
}
err = UnionClose(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Println("请求成功")
}
func UnionClose(config *wxpay_utility.MchConfig, request *UnionCloseRequest) (err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/combine-transactions/out-trade-no/{combine_out_trade_no}/close"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return err
}
reqUrl.Path = strings.Replace(reqUrl.Path, "{combine_out_trade_no}", url.PathEscape(*request.CombineOutTradeNo), -1)
reqBody, err := json.Marshal(request)
if err != nil {
return err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return err
}
return nil
} else {
return wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type UnionCloseRequest struct {
CombineAppid *string `json:"combine_appid,omitempty"`
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
SubOrders []UnionCloseSubOrder `json:"sub_orders,omitempty"`
}
func (o *UnionCloseRequest) MarshalJSON() ([]byte, error) {
type Alias UnionCloseRequest
a := &struct {
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
CombineOutTradeNo: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type UnionCloseSubOrder struct {
Mchid *string `json:"mchid,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
}
@@ -0,0 +1,195 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &UnionApiv3H5PrepayRequest{
CombineAppid: wxpay_utility.String("wxd678efh567hg6787"),
CombineOutTradeNo: wxpay_utility.String("1217752501201407033233368018"),
CombineMchid: wxpay_utility.String("1230000109"),
SceneInfo: &UnionH5SceneInfo{
PayerClientIp: wxpay_utility.String("14.23.150.211"),
DeviceId: wxpay_utility.String("013467007045764"),
H5Info: &UnionH5Info{
Type: wxpay_utility.String("iOS"),
AppName: wxpay_utility.String("王者荣耀"),
AppUrl: wxpay_utility.String("https://pay.qq.com"),
BundleId: wxpay_utility.String("com.tencent.wzryiOS"),
PackageName: wxpay_utility.String("com.tencent.tmgp.sgame"),
},
},
SubOrders: []UnionCommonSubOrder{
UnionCommonSubOrder{
Mchid: wxpay_utility.String("1230000109"),
OutTradeNo: wxpay_utility.String("20150806125346"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("深圳分店"),
Description: wxpay_utility.String("腾讯充值中心-QQ会员充值"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
UnionCommonSubOrder{
Mchid: wxpay_utility.String("1230000119"),
OutTradeNo: wxpay_utility.String("20150806125347"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("广州分店"),
Description: wxpay_utility.String("腾讯充值中心-微信充值"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
},
TimeExpire: wxpay_utility.Time(time.Now()),
NotifyUrl: wxpay_utility.String("https://yourapp.com/notify"),
}
response, err := UnionH5Prepay(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func UnionH5Prepay(config *wxpay_utility.MchConfig, request *UnionApiv3H5PrepayRequest) (response *UnionApiv3H5PrepayResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/combine-transactions/h5"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &UnionApiv3H5PrepayResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type UnionApiv3H5PrepayRequest struct {
CombineAppid *string `json:"combine_appid,omitempty"`
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
CombineMchid *string `json:"combine_mchid,omitempty"`
SceneInfo *UnionH5SceneInfo `json:"scene_info,omitempty"`
SubOrders []UnionCommonSubOrder `json:"sub_orders,omitempty"`
TimeExpire *time.Time `json:"time_expire,omitempty"`
NotifyUrl *string `json:"notify_url,omitempty"`
}
type UnionApiv3H5PrepayResponse struct {
H5Url *string `json:"h5_url,omitempty"`
}
type UnionH5SceneInfo struct {
PayerClientIp *string `json:"payer_client_ip,omitempty"`
DeviceId *string `json:"device_id,omitempty"`
H5Info *UnionH5Info `json:"h5_info,omitempty"`
}
type UnionCommonSubOrder struct {
Mchid *string `json:"mchid,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
Amount *UnionAmountInfo `json:"amount,omitempty"`
Attach *string `json:"attach,omitempty"`
Description *string `json:"description,omitempty"`
GoodsTag *string `json:"goods_tag,omitempty"`
SettleInfo *UnionSettleInfo `json:"settle_info,omitempty"`
}
type UnionH5Info struct {
Type *string `json:"type,omitempty"`
AppName *string `json:"app_name,omitempty"`
AppUrl *string `json:"app_url,omitempty"`
BundleId *string `json:"bundle_id,omitempty"`
PackageName *string `json:"package_name,omitempty"`
}
type UnionAmountInfo struct {
TotalAmount *int64 `json:"total_amount,omitempty"`
Currency *string `json:"currency,omitempty"`
}
type UnionSettleInfo struct {
ProfitSharing *bool `json:"profit_sharing,omitempty"`
}
@@ -0,0 +1,191 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &UnionApiv3JsapiPrepayRequest{
CombineAppid: wxpay_utility.String("wxd678efh567hg6787"),
CombineMchid: wxpay_utility.String("1230000109"),
CombineOutTradeNo: wxpay_utility.String("1217752501201407033233368018"),
CombinePayerInfo: &UnionPayerInfo{
Openid: wxpay_utility.String("oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"),
},
SceneInfo: &UnionSceneInfo{
DeviceId: wxpay_utility.String("POS1:1"),
PayerClientIp: wxpay_utility.String("14.17.22.32"),
},
SubOrders: []UnionSubOrder{
UnionSubOrder{
Mchid: wxpay_utility.String("1230000109"),
OutTradeNo: wxpay_utility.String("20150806125346"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("深圳分店"),
Description: wxpay_utility.String("腾讯充值中心-QQ会员充值"),
Detail: wxpay_utility.String("买单费用"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
UnionSubOrder{
Mchid: wxpay_utility.String("1230000119"),
OutTradeNo: wxpay_utility.String("20150806125347"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("广州分店"),
Description: wxpay_utility.String("腾讯充值中心-微信充值"),
Detail: wxpay_utility.String("买单费用"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
},
TimeExpire: wxpay_utility.Time(time.Now()),
NotifyUrl: wxpay_utility.String("https://yourapp.com/notify"),
}
response, err := UnionJsapiPrepay(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func UnionJsapiPrepay(config *wxpay_utility.MchConfig, request *UnionApiv3JsapiPrepayRequest) (response *UnionApiv3JsapiPrepayResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/combine-transactions/jsapi"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &UnionApiv3JsapiPrepayResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type UnionApiv3JsapiPrepayRequest struct {
CombineAppid *string `json:"combine_appid,omitempty"`
CombineMchid *string `json:"combine_mchid,omitempty"`
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
CombinePayerInfo *UnionPayerInfo `json:"combine_payer_info,omitempty"`
SceneInfo *UnionSceneInfo `json:"scene_info,omitempty"`
SubOrders []UnionSubOrder `json:"sub_orders,omitempty"`
TimeExpire *time.Time `json:"time_expire,omitempty"`
NotifyUrl *string `json:"notify_url,omitempty"`
}
type UnionApiv3JsapiPrepayResponse struct {
PrepayId *string `json:"prepay_id,omitempty"`
}
type UnionPayerInfo struct {
Openid *string `json:"openid,omitempty"`
SubOpenid *string `json:"sub_openid,omitempty"`
}
type UnionSceneInfo struct {
DeviceId *string `json:"device_id,omitempty"`
PayerClientIp *string `json:"payer_client_ip,omitempty"`
}
type UnionSubOrder struct {
Mchid *string `json:"mchid,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
Amount *UnionAmountInfo `json:"amount,omitempty"`
Attach *string `json:"attach,omitempty"`
Description *string `json:"description,omitempty"`
Detail *string `json:"detail,omitempty"`
GoodsTag *string `json:"goods_tag,omitempty"`
SettleInfo *UnionSettleInfo `json:"settle_info,omitempty"`
}
type UnionAmountInfo struct {
TotalAmount *int64 `json:"total_amount,omitempty"`
Currency *string `json:"currency,omitempty"`
}
type UnionSettleInfo struct {
ProfitSharing *bool `json:"profit_sharing,omitempty"`
}
@@ -0,0 +1,179 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &UnionApiv3NativePrepayRequest{
CombineAppid: wxpay_utility.String("wxd678efh567hg6787"),
CombineOutTradeNo: wxpay_utility.String("20150806125346"),
CombineMchid: wxpay_utility.String("1900000109"),
SceneInfo: &UnionSceneInfo{
DeviceId: wxpay_utility.String("POS1:1"),
PayerClientIp: wxpay_utility.String("14.17.22.32"),
},
SubOrders: []UnionCommonSubOrder{
UnionCommonSubOrder{
Mchid: wxpay_utility.String("1230000109"),
OutTradeNo: wxpay_utility.String("20150806125346"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("深圳分店"),
Description: wxpay_utility.String("腾讯充值中心-QQ会员充值"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
UnionCommonSubOrder{
Mchid: wxpay_utility.String("1230000119"),
OutTradeNo: wxpay_utility.String("20150806125347"),
Amount: &UnionAmountInfo{
TotalAmount: wxpay_utility.Int64(10),
Currency: wxpay_utility.String("CNY"),
},
Attach: wxpay_utility.String("广州分店"),
Description: wxpay_utility.String("腾讯充值中心-微信充值"),
GoodsTag: wxpay_utility.String("WXG"),
SettleInfo: &UnionSettleInfo{
ProfitSharing: wxpay_utility.Bool(false),
},
},
},
TimeExpire: wxpay_utility.Time(time.Now()),
NotifyUrl: wxpay_utility.String("https://yourapp.com/notify"),
}
response, err := UnionNativePrepay(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func UnionNativePrepay(config *wxpay_utility.MchConfig, request *UnionApiv3NativePrepayRequest) (response *UnionApiv3NativePrepayResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/combine-transactions/native"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &UnionApiv3NativePrepayResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type UnionApiv3NativePrepayRequest struct {
CombineAppid *string `json:"combine_appid,omitempty"`
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
CombineMchid *string `json:"combine_mchid,omitempty"`
SceneInfo *UnionSceneInfo `json:"scene_info,omitempty"`
SubOrders []UnionCommonSubOrder `json:"sub_orders,omitempty"`
TimeExpire *time.Time `json:"time_expire,omitempty"`
NotifyUrl *string `json:"notify_url,omitempty"`
}
type UnionApiv3NativePrepayResponse struct {
CodeUrl *string `json:"code_url,omitempty"`
}
type UnionSceneInfo struct {
DeviceId *string `json:"device_id,omitempty"`
PayerClientIp *string `json:"payer_client_ip,omitempty"`
}
type UnionCommonSubOrder struct {
Mchid *string `json:"mchid,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
Amount *UnionAmountInfo `json:"amount,omitempty"`
Attach *string `json:"attach,omitempty"`
Description *string `json:"description,omitempty"`
GoodsTag *string `json:"goods_tag,omitempty"`
SettleInfo *UnionSettleInfo `json:"settle_info,omitempty"`
}
type UnionAmountInfo struct {
TotalAmount *int64 `json:"total_amount,omitempty"`
Currency *string `json:"currency,omitempty"`
}
type UnionSettleInfo struct {
ProfitSharing *bool `json:"profit_sharing,omitempty"`
}
@@ -0,0 +1,178 @@
package main
import (
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &UnionQueryByOutTradeNoRequest{
CombineOutTradeNo: wxpay_utility.String("P20150806125346"),
}
response, err := UnionQueryByOutTradeNo(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func UnionQueryByOutTradeNo(config *wxpay_utility.MchConfig, request *UnionQueryByOutTradeNoRequest) (response *UnionApiv3UnionQueryResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "GET"
path = "/v3/combine-transactions/out-trade-no/{combine_out_trade_no}"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqUrl.Path = strings.Replace(reqUrl.Path, "{combine_out_trade_no}", url.PathEscape(*request.CombineOutTradeNo), -1)
httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &UnionApiv3UnionQueryResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type UnionQueryByOutTradeNoRequest struct {
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
}
func (o *UnionQueryByOutTradeNoRequest) MarshalJSON() ([]byte, error) {
type Alias UnionQueryByOutTradeNoRequest
a := &struct {
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
CombineOutTradeNo: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type UnionApiv3UnionQueryResponse struct {
CombineAppid *string `json:"combine_appid,omitempty"`
CombineMchid *string `json:"combine_mchid,omitempty"`
CombineOutTradeNo *string `json:"combine_out_trade_no,omitempty"`
CombinePayerInfo *UnionCommRespPayerInfo `json:"combine_payer_info,omitempty"`
SceneInfo *UnionCommRespSceneInfo `json:"scene_info,omitempty"`
SubOrders []UnionSubOrder `json:"sub_orders,omitempty"`
}
type UnionCommRespPayerInfo struct {
Openid *string `json:"openid,omitempty"`
}
type UnionCommRespSceneInfo struct {
DeviceId *string `json:"device_id,omitempty"`
}
type UnionSubOrder struct {
Mchid *string `json:"mchid,omitempty"`
SubMchid *string `json:"sub_mchid,omitempty"`
SubAppid *string `json:"sub_appid,omitempty"`
SubOpenid *string `json:"sub_openid,omitempty"`
OutTradeNo *string `json:"out_trade_no,omitempty"`
TransactionId *string `json:"transaction_id,omitempty"`
TradeType *string `json:"trade_type,omitempty"`
TradeState *string `json:"trade_state,omitempty"`
BankType *string `json:"bank_type,omitempty"`
Attach *string `json:"attach,omitempty"`
SuccessTime *string `json:"success_time,omitempty"`
Amount *UnionCommRespAmountInfo `json:"amount,omitempty"`
PromotionDetail []UnionPromotionDetail `json:"promotion_detail,omitempty"`
}
type UnionCommRespAmountInfo struct {
TotalAmount *int64 `json:"total_amount,omitempty"`
PayerAmount *int64 `json:"payer_amount,omitempty"`
Currency *string `json:"currency,omitempty"`
PayerCurrency *string `json:"payer_currency,omitempty"`
SettlementRate *int64 `json:"settlement_rate,omitempty"`
}
type UnionPromotionDetail struct {
CouponId *string `json:"coupon_id,omitempty"`
Name *string `json:"name,omitempty"`
Scope *string `json:"scope,omitempty"`
Type *string `json:"type,omitempty"`
Amount *int64 `json:"amount,omitempty"`
StockId *string `json:"stock_id,omitempty"`
WechatpayContribute *int64 `json:"wechatpay_contribute,omitempty"`
MerchantContribute *int64 `json:"merchant_contribute,omitempty"`
OtherContribute *int64 `json:"other_contribute,omitempty"`
Currency *string `json:"currency,omitempty"`
GoodsDetail []GoodsDetailInPromotion `json:"goods_detail,omitempty"`
}
type GoodsDetailInPromotion struct {
GoodsId *string `json:"goods_id,omitempty"`
Quantity *int64 `json:"quantity,omitempty"`
UnitPrice *int64 `json:"unit_price,omitempty"`
DiscountAmount *int64 `json:"discount_amount,omitempty"`
GoodsRemark *string `json:"goods_remark,omitempty"`
}
@@ -0,0 +1,156 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &AddReceiverRequest{
Appid: wxpay_utility.String("wx8888888888888888"),
Type: RECEIVERTYPE_MERCHANT_ID.Ptr(),
Account: wxpay_utility.String("86693852"),
Name: wxpay_utility.String("hu89ohu89ohu89o"), /*请传入wxpay_utility.EncryptOAEPWithPublicKey 加密结果*/
RelationType: RECEIVERRELATIONTYPE_STORE.Ptr(),
CustomRelation: wxpay_utility.String("代理商"),
}
response, err := AddReceiver(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func AddReceiver(config *wxpay_utility.MchConfig, request *AddReceiverRequest) (response *AddReceiverResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/profitsharing/receivers/add"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &AddReceiverResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type AddReceiverRequest struct {
Appid *string `json:"appid,omitempty"`
Type *ReceiverType `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
Name *string `json:"name,omitempty"`
RelationType *ReceiverRelationType `json:"relation_type,omitempty"`
CustomRelation *string `json:"custom_relation,omitempty"`
}
type AddReceiverResponse struct {
Type *ReceiverType `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
Name *string `json:"name,omitempty"`
RelationType *ReceiverRelationType `json:"relation_type,omitempty"`
CustomRelation *string `json:"custom_relation,omitempty"`
}
type ReceiverType string
func (e ReceiverType) Ptr() *ReceiverType {
return &e
}
const (
RECEIVERTYPE_MERCHANT_ID ReceiverType = "MERCHANT_ID"
RECEIVERTYPE_PERSONAL_OPENID ReceiverType = "PERSONAL_OPENID"
)
type ReceiverRelationType string
func (e ReceiverRelationType) Ptr() *ReceiverRelationType {
return &e
}
const (
RECEIVERRELATIONTYPE_STORE ReceiverRelationType = "STORE"
RECEIVERRELATIONTYPE_STAFF ReceiverRelationType = "STAFF"
RECEIVERRELATIONTYPE_STORE_OWNER ReceiverRelationType = "STORE_OWNER"
RECEIVERRELATIONTYPE_PARTNER ReceiverRelationType = "PARTNER"
RECEIVERRELATIONTYPE_HEADQUARTER ReceiverRelationType = "HEADQUARTER"
RECEIVERRELATIONTYPE_BRAND ReceiverRelationType = "BRAND"
RECEIVERRELATIONTYPE_DISTRIBUTOR ReceiverRelationType = "DISTRIBUTOR"
RECEIVERRELATIONTYPE_USER ReceiverRelationType = "USER"
RECEIVERRELATIONTYPE_SUPPLIER ReceiverRelationType = "SUPPLIER"
RECEIVERRELATIONTYPE_CUSTOM ReceiverRelationType = "CUSTOM"
)
删除分账接收方
@@ -0,0 +1,178 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx",
"1DDE55AD98Exxxxxxxxxx",
"/path/to/apiclient_key.pem",
"PUB_KEY_ID_xxxxxxxxxxxxx",
"/path/to/wxp_pub.pem",
)
if err != nil {
fmt.Println(err)
return
}
request := &CreateOrderRequest{
Appid: wxpay_utility.String("wx8888888888888888"),
TransactionId: wxpay_utility.String("4208450740201411110007820472"),
OutOrderNo: wxpay_utility.String("P20150806125346"),
Receivers: []CreateOrderReceiver{CreateOrderReceiver{
Type: wxpay_utility.String("MERCHANT_ID"),
Account: wxpay_utility.String("86693852"),
Name: wxpay_utility.String("hu89ohu89ohu89o"), /*请传入wxpay_utility.EncryptOAEPWithPublicKey 加密结果*/
Amount: wxpay_utility.Int64(888),
Description: wxpay_utility.String("分给商户A"),
}},
UnfreezeUnsplit: wxpay_utility.Bool(true),
}
response, err := CreateOrder(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
return
}
fmt.Printf("请求成功: %+v\n", response)
}
func CreateOrder(config *wxpay_utility.MchConfig, request *CreateOrderRequest) (response *OrdersEntity, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/profitsharing/orders"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
err = wxpay_utility.ValidateResponse(config.WechatPayPublicKeyId(), config.WechatPayPublicKey(), &httpResponse.Header, respBody)
if err != nil {
return nil, err
}
response := &OrdersEntity{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(httpResponse.StatusCode, httpResponse.Header, respBody)
}
}
type CreateOrderRequest struct {
Appid *string `json:"appid,omitempty"`
TransactionId *string `json:"transaction_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
Receivers []CreateOrderReceiver `json:"receivers,omitempty"`
UnfreezeUnsplit *bool `json:"unfreeze_unsplit,omitempty"`
}
type OrdersEntity struct {
TransactionId *string `json:"transaction_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
OrderId *string `json:"order_id,omitempty"`
State *OrderStatus `json:"state,omitempty"`
Receivers []OrderReceiverDetail `json:"receivers,omitempty"`
}
type CreateOrderReceiver struct {
Type *string `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
Name *string `json:"name,omitempty"`
Amount *int64 `json:"amount,omitempty"`
Description *string `json:"description,omitempty"`
}
type OrderStatus string
func (e OrderStatus) Ptr() *OrderStatus { return &e }
const (
ORDERSTATUS_PROCESSING OrderStatus = "PROCESSING"
ORDERSTATUS_FINISHED OrderStatus = "FINISHED"
)
type OrderReceiverDetail struct {
Amount *int64 `json:"amount,omitempty"`
Description *string `json:"description,omitempty"`
Type *ReceiverType `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
Result *DetailStatus `json:"result,omitempty"`
FailReason *DetailFailReason `json:"fail_reason,omitempty"`
CreateTime *time.Time `json:"create_time,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"`
DetailId *string `json:"detail_id,omitempty"`
}
type ReceiverType string
func (e ReceiverType) Ptr() *ReceiverType { return &e }
const (
RECEIVERTYPE_MERCHANT_ID ReceiverType = "MERCHANT_ID"
RECEIVERTYPE_PERSONAL_OPENID ReceiverType = "PERSONAL_OPENID"
)
type DetailStatus string
func (e DetailStatus) Ptr() *DetailStatus { return &e }
const (
DETAILSTATUS_PENDING DetailStatus = "PENDING"
DETAILSTATUS_SUCCESS DetailStatus = "SUCCESS"
DETAILSTATUS_CLOSED DetailStatus = "CLOSED"
)
type DetailFailReason string
func (e DetailFailReason) Ptr() *DetailFailReason { return &e }
const (
DETAILFAILREASON_ACCOUNT_ABNORMAL DetailFailReason = "ACCOUNT_ABNORMAL"
DETAILFAILREASON_NO_RELATION DetailFailReason = "NO_RELATION"
DETAILFAILREASON_RECEIVER_HIGH_RISK DetailFailReason = "RECEIVER_HIGH_RISK"
DETAILFAILREASON_RECEIVER_REAL_NAME_NOT_VERIFIED DetailFailReason = "RECEIVER_REAL_NAME_NOT_VERIFIED"
DETAILFAILREASON_NO_AUTH DetailFailReason = "NO_AUTH"
DETAILFAILREASON_RECEIVER_RECEIPT_LIMIT DetailFailReason = "RECEIVER_RECEIPT_LIMIT"
DETAILFAILREASON_PAYER_ACCOUNT_ABNORMAL DetailFailReason = "PAYER_ACCOUNT_ABNORMAL"
DETAILFAILREASON_INVALID_REQUEST DetailFailReason = "INVALID_REQUEST"
)
@@ -0,0 +1,159 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &CreateReturnOrderRequest{
OrderId: wxpay_utility.String("3008450740201411110007820472"),
OutOrderNo: wxpay_utility.String("P20150806125346"),
OutReturnNo: wxpay_utility.String("R20190516001"),
ReturnMchid: wxpay_utility.String("86693852"),
Amount: wxpay_utility.Int64(10),
Description: wxpay_utility.String("用户退款"),
}
response, err := CreateReturnOrder(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func CreateReturnOrder(config *wxpay_utility.MchConfig, request *CreateReturnOrderRequest) (response *ReturnOrdersEntity, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/profitsharing/return-orders"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &ReturnOrdersEntity{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type CreateReturnOrderRequest struct {
OrderId *string `json:"order_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
OutReturnNo *string `json:"out_return_no,omitempty"`
ReturnMchid *string `json:"return_mchid,omitempty"`
Amount *int64 `json:"amount,omitempty"`
Description *string `json:"description,omitempty"`
}
type ReturnOrdersEntity struct {
OrderId *string `json:"order_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
OutReturnNo *string `json:"out_return_no,omitempty"`
ReturnId *string `json:"return_id,omitempty"`
ReturnMchid *string `json:"return_mchid,omitempty"`
Amount *int64 `json:"amount,omitempty"`
Description *string `json:"description,omitempty"`
Result *ReturnOrderStatus `json:"result,omitempty"`
FailReason *ReturnOrderFailReason `json:"fail_reason,omitempty"`
CreateTime *time.Time `json:"create_time,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"`
}
type ReturnOrderStatus string
func (e ReturnOrderStatus) Ptr() *ReturnOrderStatus {
return &e
}
const (
RETURNORDERSTATUS_PROCESSING ReturnOrderStatus = "PROCESSING"
RETURNORDERSTATUS_SUCCESS ReturnOrderStatus = "SUCCESS"
RETURNORDERSTATUS_FAILED ReturnOrderStatus = "FAILED"
)
type ReturnOrderFailReason string
func (e ReturnOrderFailReason) Ptr() *ReturnOrderFailReason {
return &e
}
const (
RETURNORDERFAILREASON_ACCOUNT_ABNORMAL ReturnOrderFailReason = "ACCOUNT_ABNORMAL"
RETURNORDERFAILREASON_BALANCE_NOT_ENOUGH ReturnOrderFailReason = "BALANCE_NOT_ENOUGH"
RETURNORDERFAILREASON_TIME_OUT_CLOSED ReturnOrderFailReason = "TIME_OUT_CLOSED"
RETURNORDERFAILREASON_PAYER_ACCOUNT_ABNORMAL ReturnOrderFailReason = "PAYER_ACCOUNT_ABNORMAL"
RETURNORDERFAILREASON_INVALID_REQUEST ReturnOrderFailReason = "INVALID_REQUEST"
)
查询分账回退结果
@@ -0,0 +1,128 @@
package main
import (
"bytes"
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &DeleteReceiverRequest{
Appid: wxpay_utility.String("wx8888888888888888"),
Type: RECEIVERTYPE_MERCHANT_ID.Ptr(),
Account: wxpay_utility.String("1900000109"),
}
response, err := DeleteReceiver(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func DeleteReceiver(config *wxpay_utility.MchConfig, request *DeleteReceiverRequest) (response *DeleteReceiverResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "POST"
path = "/v3/profitsharing/receivers/delete"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqBody, err := json.Marshal(request)
if err != nil {
return nil, err
}
httpRequest, err := http.NewRequest(method, reqUrl.String(), bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
httpRequest.Header.Set("Content-Type", "application/json")
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), reqBody)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &DeleteReceiverResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type DeleteReceiverRequest struct {
Appid *string `json:"appid,omitempty"`
Type *ReceiverType `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
}
type DeleteReceiverResponse struct {
Type *ReceiverType `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
}
type ReceiverType string
func (e ReceiverType) Ptr() *ReceiverType {
return &e
}
const (
RECEIVERTYPE_MERCHANT_ID ReceiverType = "MERCHANT_ID"
RECEIVERTYPE_PERSONAL_OPENID ReceiverType = "PERSONAL_OPENID"
)
申请分账账单
@@ -0,0 +1,198 @@
package main
import (
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &QueryOrderRequest{
TransactionId: wxpay_utility.String("4208450740201411110007820472"),
OutOrderNo: wxpay_utility.String("P20150806125346"),
}
response, err := QueryOrder(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func QueryOrder(config *wxpay_utility.MchConfig, request *QueryOrderRequest) (response *OrdersEntity, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "GET"
path = "/v3/profitsharing/orders/{out_order_no}"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqUrl.Path = strings.Replace(reqUrl.Path, "{out_order_no}", url.PathEscape(*request.OutOrderNo), -1)
query := reqUrl.Query()
if request.TransactionId != nil {
query.Add("transaction_id", *request.TransactionId)
}
reqUrl.RawQuery = query.Encode()
httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &OrdersEntity{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type QueryOrderRequest struct {
TransactionId *string `json:"transaction_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
}
func (o *QueryOrderRequest) MarshalJSON() ([]byte, error) {
type Alias QueryOrderRequest
a := &struct {
TransactionId *string `json:"transaction_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
TransactionId: nil,
OutOrderNo: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type OrdersEntity struct {
TransactionId *string `json:"transaction_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
OrderId *string `json:"order_id,omitempty"`
State *OrderStatus `json:"state,omitempty"`
Receivers []OrderReceiverDetail `json:"receivers,omitempty"`
}
type OrderStatus string
func (e OrderStatus) Ptr() *OrderStatus {
return &e
}
const (
ORDERSTATUS_PROCESSING OrderStatus = "PROCESSING"
ORDERSTATUS_FINISHED OrderStatus = "FINISHED"
)
type OrderReceiverDetail struct {
Amount *int64 `json:"amount,omitempty"`
Description *string `json:"description,omitempty"`
Type *ReceiverType `json:"type,omitempty"`
Account *string `json:"account,omitempty"`
Result *DetailStatus `json:"result,omitempty"`
FailReason *DetailFailReason `json:"fail_reason,omitempty"`
CreateTime *time.Time `json:"create_time,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"`
DetailId *string `json:"detail_id,omitempty"`
}
type ReceiverType string
func (e ReceiverType) Ptr() *ReceiverType {
return &e
}
const (
RECEIVERTYPE_MERCHANT_ID ReceiverType = "MERCHANT_ID"
RECEIVERTYPE_PERSONAL_OPENID ReceiverType = "PERSONAL_OPENID"
)
type DetailStatus string
func (e DetailStatus) Ptr() *DetailStatus {
return &e
}
const (
DETAILSTATUS_PENDING DetailStatus = "PENDING"
DETAILSTATUS_SUCCESS DetailStatus = "SUCCESS"
DETAILSTATUS_CLOSED DetailStatus = "CLOSED"
)
type DetailFailReason string
func (e DetailFailReason) Ptr() *DetailFailReason {
return &e
}
const (
DETAILFAILREASON_ACCOUNT_ABNORMAL DetailFailReason = "ACCOUNT_ABNORMAL"
DETAILFAILREASON_NO_RELATION DetailFailReason = "NO_RELATION"
DETAILFAILREASON_RECEIVER_HIGH_RISK DetailFailReason = "RECEIVER_HIGH_RISK"
DETAILFAILREASON_RECEIVER_REAL_NAME_NOT_VERIFIED DetailFailReason = "RECEIVER_REAL_NAME_NOT_VERIFIED"
DETAILFAILREASON_NO_AUTH DetailFailReason = "NO_AUTH"
DETAILFAILREASON_RECEIVER_RECEIPT_LIMIT DetailFailReason = "RECEIVER_RECEIPT_LIMIT"
DETAILFAILREASON_PAYER_ACCOUNT_ABNORMAL DetailFailReason = "PAYER_ACCOUNT_ABNORMAL"
DETAILFAILREASON_INVALID_REQUEST DetailFailReason = "INVALID_REQUEST"
)
请求分账回退
@@ -0,0 +1,122 @@
package main
import (
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &QueryOrderAmountRequest{
TransactionId: wxpay_utility.String("4208450740201411110007820472"),
}
response, err := QueryOrderAmount(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func QueryOrderAmount(config *wxpay_utility.MchConfig, request *QueryOrderAmountRequest) (response *QueryOrderAmountResponse, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "GET"
path = "/v3/profitsharing/transactions/{transaction_id}/amounts"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqUrl.Path = strings.Replace(reqUrl.Path, "{transaction_id}", url.PathEscape(*request.TransactionId), -1)
httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &QueryOrderAmountResponse{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type QueryOrderAmountRequest struct {
TransactionId *string `json:"transaction_id,omitempty"`
}
func (o *QueryOrderAmountRequest) MarshalJSON() ([]byte, error) {
type Alias QueryOrderAmountRequest
a := &struct {
TransactionId *string `json:"transaction_id,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
TransactionId: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type QueryOrderAmountResponse struct {
TransactionId *string `json:"transaction_id,omitempty"`
UnsplitAmount *int64 `json:"unsplit_amount,omitempty"`
}
添加分账接收方
@@ -0,0 +1,167 @@
package main
import (
"demo/wxpay_utility" // 引用微信支付工具库,参考 https://pay.weixin.qq.com/doc/v3/merchant/4015119334
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"
)
func main() {
// TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
config, err := wxpay_utility.CreateMchConfig(
"19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
"1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
"/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
"PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
"/path/to/wxp_pub.pem", // 微信支付公钥文件路径,本地文件路径
)
if err != nil {
fmt.Println(err)
return
}
request := &QueryReturnOrderRequest{
OutReturnNo: wxpay_utility.String("R20190516001"),
OutOrderNo: wxpay_utility.String("P20190806125346"),
}
response, err := QueryReturnOrder(config, request)
if err != nil {
fmt.Printf("请求失败: %+v\n", err)
// TODO: 请求失败,根据状态码执行不同的处理
return
}
// TODO: 请求成功,继续业务逻辑
fmt.Printf("请求成功: %+v\n", response)
}
func QueryReturnOrder(config *wxpay_utility.MchConfig, request *QueryReturnOrderRequest) (response *ReturnOrdersEntity, err error) {
const (
host = "https://api.mch.weixin.qq.com"
method = "GET"
path = "/v3/profitsharing/return-orders/{out_return_no}"
)
reqUrl, err := url.Parse(fmt.Sprintf("%s%s", host, path))
if err != nil {
return nil, err
}
reqUrl.Path = strings.Replace(reqUrl.Path, "{out_return_no}", url.PathEscape(*request.OutReturnNo), -1)
query := reqUrl.Query()
if request.OutOrderNo != nil {
query.Add("out_order_no", *request.OutOrderNo)
}
reqUrl.RawQuery = query.Encode()
httpRequest, err := http.NewRequest(method, reqUrl.String(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Accept", "application/json")
httpRequest.Header.Set("Wechatpay-Serial", config.WechatPayPublicKeyId())
authorization, err := wxpay_utility.BuildAuthorization(config.MchId(), config.CertificateSerialNo(), config.PrivateKey(), method, reqUrl.RequestURI(), nil)
if err != nil {
return nil, err
}
httpRequest.Header.Set("Authorization", authorization)
client := &http.Client{}
httpResponse, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
respBody, err := wxpay_utility.ExtractResponseBody(httpResponse)
if err != nil {
return nil, err
}
if httpResponse.StatusCode >= 200 && httpResponse.StatusCode < 300 {
// 2XX 成功,验证应答签名
err = wxpay_utility.ValidateResponse(
config.WechatPayPublicKeyId(),
config.WechatPayPublicKey(),
&httpResponse.Header,
respBody,
)
if err != nil {
return nil, err
}
response := &ReturnOrdersEntity{}
if err := json.Unmarshal(respBody, response); err != nil {
return nil, err
}
return response, nil
} else {
return nil, wxpay_utility.NewApiException(
httpResponse.StatusCode,
httpResponse.Header,
respBody,
)
}
}
type QueryReturnOrderRequest struct {
OutReturnNo *string `json:"out_return_no,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
}
func (o *QueryReturnOrderRequest) MarshalJSON() ([]byte, error) {
type Alias QueryReturnOrderRequest
a := &struct {
OutReturnNo *string `json:"out_return_no,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
*Alias
}{
// 序列化时移除非 Body 字段
OutReturnNo: nil,
OutOrderNo: nil,
Alias: (*Alias)(o),
}
return json.Marshal(a)
}
type ReturnOrdersEntity struct {
OrderId *string `json:"order_id,omitempty"`
OutOrderNo *string `json:"out_order_no,omitempty"`
OutReturnNo *string `json:"out_return_no,omitempty"`
ReturnId *string `json:"return_id,omitempty"`
ReturnMchid *string `json:"return_mchid,omitempty"`
Amount *int64 `json:"amount,omitempty"`
Description *string `json:"description,omitempty"`
Result *ReturnOrderStatus `json:"result,omitempty"`
FailReason *ReturnOrderFailReason `json:"fail_reason,omitempty"`
CreateTime *time.Time `json:"create_time,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"`
}
type ReturnOrderStatus string
func (e ReturnOrderStatus) Ptr() *ReturnOrderStatus {
return &e
}
const (
RETURNORDERSTATUS_PROCESSING ReturnOrderStatus = "PROCESSING"
RETURNORDERSTATUS_SUCCESS ReturnOrderStatus = "SUCCESS"
RETURNORDERSTATUS_FAILED ReturnOrderStatus = "FAILED"
)
type ReturnOrderFailReason string
func (e ReturnOrderFailReason) Ptr() *ReturnOrderFailReason {
return &e
}
const (
RETURNORDERFAILREASON_ACCOUNT_ABNORMAL ReturnOrderFailReason = "ACCOUNT_ABNORMAL"
RETURNORDERFAILREASON_BALANCE_NOT_ENOUGH ReturnOrderFailReason = "BALANCE_NOT_ENOUGH"
RETURNORDERFAILREASON_TIME_OUT_CLOSED ReturnOrderFailReason = "TIME_OUT_CLOSED"
RETURNORDERFAILREASON_PAYER_ACCOUNT_ABNORMAL ReturnOrderFailReason = "PAYER_ACCOUNT_ABNORMAL"
RETURNORDERFAILREASON_INVALID_REQUEST ReturnOrderFailReason = "INVALID_REQUEST"
)
解冻剩余资金

Some files were not shown because too many files have changed in this diff Show More