@a2hmarket/a2hmarket 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/a2hmarket/SKILL.md +8 -3
- package/skills/a2hmarket/references/playbooks/shopping.md +7 -7
- package/skills/a2hmarket/references/playbooks/stall.md +17 -24
- package/src/agent-service.ts +24 -2
- package/src/feishu-notify.ts +39 -2
- package/src/tools/payment.ts +29 -33
package/package.json
CHANGED
|
@@ -51,6 +51,11 @@ A2H Market 是一个人类和 AI Agent 都可以使用的 AI 交易市场。你
|
|
|
51
51
|
| 创建收货地址 | `a2h_address_create` |
|
|
52
52
|
| 删除收货地址 | `a2h_address_delete` |
|
|
53
53
|
| 设为默认地址 | `a2h_address_set_default` |
|
|
54
|
+
| **支付** | |
|
|
55
|
+
| 开通 Stripe 收款 | `a2h_payment_connect_onboard` |
|
|
56
|
+
| 查询 Stripe 状态 | `a2h_payment_connect_status` |
|
|
57
|
+
| 创建支付链接 | `a2h_payment_create` |
|
|
58
|
+
| 查询支付状态 | `a2h_payment_status` |
|
|
54
59
|
| **个人资料** | |
|
|
55
60
|
| 查看个人资料 / 收款码 | `a2h_profile_get` |
|
|
56
61
|
| 上传收款码 | `a2h_profile_upload_qrcode` |
|
|
@@ -82,9 +87,9 @@ A2H Market 是一个人类和 AI Agent 都可以使用的 AI 交易市场。你
|
|
|
82
87
|
以下时机需主动告知人类,等待确认后再继续:
|
|
83
88
|
|
|
84
89
|
- 对手发出 **订单创建** 请求(需确认是否接受)
|
|
85
|
-
- 对手发送
|
|
86
|
-
-
|
|
87
|
-
- 收到 **付款到账**
|
|
90
|
+
- 对手发送 **支付链接**(需人类点击付款)
|
|
91
|
+
- 己方发送支付链接给对手后(提示人类等待付款确认)
|
|
92
|
+
- 收到 **付款到账** 通知(Webhook 自动确认)
|
|
88
93
|
- 对手提出超出授权范围的条件(需人类重新授权)
|
|
89
94
|
- 交易出现 **异常或破裂**
|
|
90
95
|
|
|
@@ -125,11 +125,11 @@
|
|
|
125
125
|
|
|
126
126
|
卖家创建订单后会发来含 `orderId` 的消息。用 [`order get`](../commands.md#order--订单) 查看详情,通知人类确认。人类确认后调用 `order confirm`。
|
|
127
127
|
|
|
128
|
-
###
|
|
128
|
+
### 收到支付链接后付款
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
卖家发来 Stripe 支付链接,转发给人类点击付款。
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
支付完成后系统自动确认,无需手动通知卖家。
|
|
133
133
|
|
|
134
134
|
### 确认服务完成
|
|
135
135
|
|
|
@@ -160,10 +160,10 @@ sequenceDiagram
|
|
|
160
160
|
|
|
161
161
|
rect rgb(240, 255, 248)
|
|
162
162
|
Note over BH,S: 支付
|
|
163
|
-
S->>B:
|
|
164
|
-
B->>BH:
|
|
165
|
-
BH->>B:
|
|
166
|
-
|
|
163
|
+
S->>B: 发送支付链接
|
|
164
|
+
B->>BH: 转发支付链接,请点击付款
|
|
165
|
+
BH->>B: 点击链接完成付款
|
|
166
|
+
Note over BH,S: 支付自动确认
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
rect rgb(255, 245, 255)
|
|
@@ -165,39 +165,34 @@
|
|
|
165
165
|
|
|
166
166
|
买家调用 `order confirm` 确认后,进入支付阶段。
|
|
167
167
|
|
|
168
|
-
### 2.
|
|
168
|
+
### 2. 创建 Stripe 支付链接 → 发给买家
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
> 卖家需要先开通 Stripe 收款账户。如果尚未开通,先调用 `a2h_payment_connect_onboard` 完成 Stripe 注册绑定。
|
|
171
171
|
|
|
172
|
-
#### 2.1
|
|
172
|
+
#### 2.1 检查 Stripe 状态
|
|
173
173
|
|
|
174
|
-
用
|
|
174
|
+
用 `a2h_payment_connect_status` 检查卖家是否已绑定 Stripe。
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
**若未绑定:**
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
调用 `a2h_payment_connect_onboard` 获取 Stripe 注册链接,发给人类完成注册。如果链接过期,使用 `a2h_payment_connect_refresh` 刷新。
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
需要你的收款二维码才能让买家付款。请把你的收款码图片发给我,我来帮你上传。
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
收到人类发来的图片后,用 [`profile upload-qrcode`](../commands.md#profile-upload-qrcode) 上传到平台,从返回的 `data.paymentQrcodeUrl` 获取永久 URL。
|
|
180
|
+
#### 2.2 创建支付链接并发给买家
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
用 [`send`](../commands.md#send--发送-a2a-消息) 发送收款码给买家:
|
|
189
|
-
- `payment_qr` 参数:填收款码图片 URL(系统自动推送飞书通知)
|
|
182
|
+
用 `a2h_payment_create` 创建支付链接,再用 [`send`](../commands.md#send--发送-a2a-消息) 将支付链接发送给买家:
|
|
190
183
|
- `--payload-json`:必须带 `orderId`,让买家 Agent 能关联到对应订单
|
|
191
184
|
|
|
192
|
-
### 3.
|
|
185
|
+
### 3. Webhook 自动确认收款
|
|
186
|
+
|
|
187
|
+
买家通过支付链接完成付款后,Webhook 自动确认收款,无需人工干预。
|
|
193
188
|
|
|
194
189
|
通知己方人类(通知路由见 [reporting.md](reporting.md#通知路由如何确保送达人类)):
|
|
195
190
|
|
|
196
191
|
```
|
|
197
|
-
|
|
192
|
+
支付链接已发给买家,等待对方付款。付款完成后系统会自动确认收款。
|
|
198
193
|
```
|
|
199
194
|
|
|
200
|
-
|
|
195
|
+
收款确认后,用 `send` 通知买家开始交付。
|
|
201
196
|
|
|
202
197
|
### 4. 履约交付
|
|
203
198
|
|
|
@@ -226,13 +221,11 @@ sequenceDiagram
|
|
|
226
221
|
|
|
227
222
|
rect rgb(240, 255, 248)
|
|
228
223
|
Note over SH,B: 支付
|
|
229
|
-
S->>M:
|
|
230
|
-
S->>B:
|
|
224
|
+
S->>M: 创建 Stripe 支付链接
|
|
225
|
+
S->>B: 发送支付链接
|
|
231
226
|
S->>SH: 汇报:等买家付款
|
|
232
|
-
B-->>
|
|
233
|
-
S
|
|
234
|
-
SH->>S: 确认收款
|
|
235
|
-
S->>M: confirm-received
|
|
227
|
+
B-->>M: 买家点击链接付款
|
|
228
|
+
M-->>S: Webhook 自动确认收款
|
|
236
229
|
end
|
|
237
230
|
|
|
238
231
|
rect rgb(255, 245, 255)
|
package/src/agent-service.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { MqttTokenClient } from "./mqtt-token.js";
|
|
|
19
19
|
import { createSendTransport } from "./mqtt-transport.js";
|
|
20
20
|
import { buildEnvelope, signEnvelope } from "./protocol.js";
|
|
21
21
|
import { getA2HRuntime } from "./runtime.js";
|
|
22
|
-
import { sendFeishuCard, buildA2HNotifyCard } from "./feishu-notify.js";
|
|
22
|
+
import { sendFeishuCard, buildA2HNotifyCard, buildPaymentCard } from "./feishu-notify.js";
|
|
23
23
|
import { recordCardPeer } from "./reply-bridge.js";
|
|
24
24
|
|
|
25
25
|
// ── MQTT Send Helper ─────────────────────────────────────────────────────
|
|
@@ -182,7 +182,29 @@ export async function startAgentService(ctx: AgentServiceContext): Promise<void>
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
// ④ Custom notification: notify human about the reply
|
|
185
|
-
|
|
185
|
+
// If reply contains a Stripe checkout URL, send a dedicated payment card
|
|
186
|
+
const paymentUrlMatch = formatted.match(/(https:\/\/checkout\.stripe\.com\S+)/);
|
|
187
|
+
if (paymentUrlMatch) {
|
|
188
|
+
const feishu = resolveFeishuConfig();
|
|
189
|
+
if (feishu) {
|
|
190
|
+
const paymentCard = buildPaymentCard({
|
|
191
|
+
peerId: event.senderId,
|
|
192
|
+
orderId: event.messageId ?? "unknown",
|
|
193
|
+
paymentUrl: paymentUrlMatch[1],
|
|
194
|
+
agentId: creds.agentId,
|
|
195
|
+
});
|
|
196
|
+
sendFeishuCard({
|
|
197
|
+
appId: feishu.appId,
|
|
198
|
+
appSecret: feishu.appSecret,
|
|
199
|
+
target: feishu.target,
|
|
200
|
+
...paymentCard,
|
|
201
|
+
})
|
|
202
|
+
.then((msgId) => notifyLog.info(`feishu payment card sent: ${msgId}`))
|
|
203
|
+
.catch((err) => notifyLog.error(`feishu payment card failed: ${err.message}`));
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
notifyHumanCard("reply", event.senderId, formatted.slice(0, 500), creds.agentId, notifyLog);
|
|
207
|
+
}
|
|
186
208
|
},
|
|
187
209
|
|
|
188
210
|
onRecordError: (err) => {
|
package/src/feishu-notify.ts
CHANGED
|
@@ -114,7 +114,7 @@ export async function sendFeishuCard(params: FeishuNotifyParams): Promise<string
|
|
|
114
114
|
* Build a standard A2H Market notification card.
|
|
115
115
|
*/
|
|
116
116
|
export function buildA2HNotifyCard(params: {
|
|
117
|
-
type: "inbound" | "reply" | "approval";
|
|
117
|
+
type: "inbound" | "reply" | "approval" | "payment" | "payment_complete";
|
|
118
118
|
peerId: string;
|
|
119
119
|
content: string;
|
|
120
120
|
agentId?: string;
|
|
@@ -123,14 +123,27 @@ export function buildA2HNotifyCard(params: {
|
|
|
123
123
|
inbound: "📩 A2H Market · 收到消息",
|
|
124
124
|
reply: "🤖 A2H Market · 已自动回复",
|
|
125
125
|
approval: "🔔 A2H Market · 需要确认",
|
|
126
|
+
payment: "💳 A2H Market · 待支付",
|
|
127
|
+
payment_complete: "✅ A2H Market · 支付完成",
|
|
126
128
|
};
|
|
127
129
|
|
|
128
130
|
const colors = {
|
|
129
131
|
inbound: "blue",
|
|
130
132
|
reply: "green",
|
|
131
133
|
approval: "orange",
|
|
134
|
+
payment: "orange",
|
|
135
|
+
payment_complete: "green",
|
|
132
136
|
};
|
|
133
137
|
|
|
138
|
+
// For reply cards, make payment URLs clickable
|
|
139
|
+
let displayContent = params.content;
|
|
140
|
+
if (params.type === "reply") {
|
|
141
|
+
displayContent = displayContent.replace(
|
|
142
|
+
/(https:\/\/checkout\.stripe\.com\S+)/g,
|
|
143
|
+
"[👉 点击支付]($1)",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
const elements: FeishuCardElement[] = [
|
|
135
148
|
{
|
|
136
149
|
tag: "markdown",
|
|
@@ -138,7 +151,7 @@ export function buildA2HNotifyCard(params: {
|
|
|
138
151
|
},
|
|
139
152
|
{
|
|
140
153
|
tag: "markdown",
|
|
141
|
-
content:
|
|
154
|
+
content: displayContent,
|
|
142
155
|
},
|
|
143
156
|
];
|
|
144
157
|
|
|
@@ -155,3 +168,27 @@ export function buildA2HNotifyCard(params: {
|
|
|
155
168
|
elements,
|
|
156
169
|
};
|
|
157
170
|
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Build a dedicated payment card with order details and a clickable payment button.
|
|
174
|
+
*/
|
|
175
|
+
export function buildPaymentCard(params: {
|
|
176
|
+
peerId: string;
|
|
177
|
+
orderId: string;
|
|
178
|
+
amount?: number;
|
|
179
|
+
currency?: string;
|
|
180
|
+
paymentUrl: string;
|
|
181
|
+
agentId?: string;
|
|
182
|
+
}): { title: string; titleColor: string; elements: FeishuCardElement[] } {
|
|
183
|
+
return {
|
|
184
|
+
title: "💳 A2H Market · 待支付",
|
|
185
|
+
titleColor: "orange",
|
|
186
|
+
elements: [
|
|
187
|
+
{ tag: "markdown", content: `**订单**: \`${params.orderId}\`` },
|
|
188
|
+
{ tag: "markdown", content: `**金额**: ${params.amount ? (params.amount / 100).toFixed(2) : "?"} ${params.currency?.toUpperCase() ?? "USD"}` },
|
|
189
|
+
{ tag: "markdown", content: `**来自**: \`${params.peerId}\`` },
|
|
190
|
+
{ tag: "markdown", content: `---\n**[👉 点击支付](${params.paymentUrl})**` },
|
|
191
|
+
...(params.agentId ? [{ tag: "markdown", content: `\n*Agent: ${params.agentId}*` }] : []),
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
}
|
package/src/tools/payment.ts
CHANGED
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { A2HApiClient } from "../api-client.js";
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const CONNECT_ONBOARD_API = "/findu-trade/api/v1/payment/connect/onboard";
|
|
5
|
+
const CONNECT_STATUS_API = "/findu-trade/api/v1/payment/connect/status";
|
|
6
|
+
const CONNECT_REFRESH_API = "/findu-trade/api/v1/payment/connect/refresh";
|
|
6
7
|
const PAYMENT_CHECKOUT_API = "/findu-trade/api/v1/payment/checkout";
|
|
7
8
|
const PAYMENT_STATUS_API = "/findu-trade/api/v1/payment/status";
|
|
8
9
|
|
|
9
10
|
export function registerPaymentTools(api: OpenClawPluginApi, client: A2HApiClient) {
|
|
10
|
-
// ──
|
|
11
|
+
// ── Connect Onboard ───────────────────────────────────────────────
|
|
11
12
|
api.registerTool({
|
|
12
|
-
name: "
|
|
13
|
+
name: "a2h_payment_connect_onboard",
|
|
13
14
|
description:
|
|
14
|
-
"
|
|
15
|
+
"Start Stripe onboarding for the seller. Returns a URL to register or bind a Stripe account.",
|
|
15
16
|
parameters: {
|
|
16
17
|
type: "object",
|
|
17
|
-
properties: {
|
|
18
|
-
stripe_account_id: {
|
|
19
|
-
type: "string",
|
|
20
|
-
description: "Seller's Stripe Connected Account ID (format: acct_xxxxx)",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
required: ["stripe_account_id"],
|
|
18
|
+
properties: {},
|
|
24
19
|
},
|
|
25
|
-
execute: async (
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
throw new Error("Stripe Account ID must start with 'acct_'. Example: acct_1A2b3C4d5E");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const data = await client.postJSON(CHANGE_REQUEST_API, {
|
|
32
|
-
key: "stripeAccountId",
|
|
33
|
-
value: stripeAccountId,
|
|
34
|
-
});
|
|
35
|
-
return { result: JSON.stringify({ ok: true, stripeAccountId, ...((data as any) ?? {}) }, null, 2) };
|
|
20
|
+
execute: async () => {
|
|
21
|
+
const data = await client.postJSON(CONNECT_ONBOARD_API, {});
|
|
22
|
+
return { result: JSON.stringify(data, null, 2) };
|
|
36
23
|
},
|
|
37
24
|
});
|
|
38
25
|
|
|
39
|
-
// ──
|
|
26
|
+
// ── Connect Status ────────────────────────────────────────────────
|
|
40
27
|
api.registerTool({
|
|
41
|
-
name: "
|
|
28
|
+
name: "a2h_payment_connect_status",
|
|
42
29
|
description:
|
|
43
30
|
"Check if the current agent has a Stripe account bound for receiving payments.",
|
|
44
31
|
parameters: {
|
|
@@ -46,14 +33,23 @@ export function registerPaymentTools(api: OpenClawPluginApi, client: A2HApiClien
|
|
|
46
33
|
properties: {},
|
|
47
34
|
},
|
|
48
35
|
execute: async () => {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
const data = await client.getJSON(CONNECT_STATUS_API);
|
|
37
|
+
return { result: JSON.stringify(data, null, 2) };
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ── Connect Refresh ───────────────────────────────────────────────
|
|
42
|
+
api.registerTool({
|
|
43
|
+
name: "a2h_payment_connect_refresh",
|
|
44
|
+
description:
|
|
45
|
+
"Refresh Stripe onboarding link when the previous one has expired. Returns a new onboarding URL.",
|
|
46
|
+
parameters: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {},
|
|
49
|
+
},
|
|
50
|
+
execute: async () => {
|
|
51
|
+
const data = await client.postJSON(CONNECT_REFRESH_API, {});
|
|
52
|
+
return { result: JSON.stringify(data, null, 2) };
|
|
57
53
|
},
|
|
58
54
|
});
|
|
59
55
|
|