fix(payment): return paymentUrl from adapters, strip base64 from tool output
Alipay/WeChat adapters now return the source payment URL alongside the QR base64. The generate_payment tool only returns paymentUrl (short text) to Claude API — base64 qrCodeUrl is stripped to prevent AI from dumping raw data:image into text responses. Frontend QRCodeSVG renders from paymentUrl instead of base64. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6609e50100
commit
389f975e33
|
|
@ -553,6 +553,9 @@ export class ImmigrationToolsService {
|
||||||
return { success: false, error: '生成支付失败,请稍后重试', orderId: order.id };
|
return { success: false, error: '生成支付失败,请稍后重试', orderId: order.id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paymentUrl 是支付链接(短文本),前端用 QRCodeSVG 从它生成二维码
|
||||||
|
// qrCodeUrl 是 base64 data URL(~数KB),绝不能传给 AI(会被塞进文本回复)
|
||||||
|
// 只返回 paymentUrl,永远不返回 qrCodeUrl(base64)
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
|
|
@ -560,10 +563,9 @@ export class ImmigrationToolsService {
|
||||||
amount: payment.amount,
|
amount: payment.amount,
|
||||||
currency: order.currency,
|
currency: order.currency,
|
||||||
paymentMethod: payment.method,
|
paymentMethod: payment.method,
|
||||||
qrCodeUrl: payment.qrCodeUrl,
|
|
||||||
paymentUrl: payment.paymentUrl,
|
paymentUrl: payment.paymentUrl,
|
||||||
expiresAt: payment.expiresAt,
|
expiresAt: payment.expiresAt,
|
||||||
message: `请扫描二维码支付 ¥${payment.amount} 完成${category}类别的移民资格评估服务`,
|
_ui_hint: '前端已自动渲染支付二维码,回复中不要包含任何链接、URL或二维码数据',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { OrderEntity } from '../../../domain/entities/order.entity';
|
||||||
|
|
||||||
export interface AlipayPaymentResult {
|
export interface AlipayPaymentResult {
|
||||||
qrCodeUrl: string;
|
qrCodeUrl: string;
|
||||||
|
paymentUrl: string;
|
||||||
tradeNo: string;
|
tradeNo: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,6 +50,7 @@ export class AlipayAdapter {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
qrCodeUrl: qrCodeDataUrl,
|
qrCodeUrl: qrCodeDataUrl,
|
||||||
|
paymentUrl: mockPaymentUrl,
|
||||||
tradeNo: `ALIPAY_${Date.now()}`,
|
tradeNo: `ALIPAY_${Date.now()}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { OrderEntity } from '../../../domain/entities/order.entity';
|
||||||
|
|
||||||
export interface WechatPaymentResult {
|
export interface WechatPaymentResult {
|
||||||
qrCodeUrl: string;
|
qrCodeUrl: string;
|
||||||
|
paymentUrl: string;
|
||||||
prepayId: string;
|
prepayId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,6 +50,7 @@ export class WechatPayAdapter {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
qrCodeUrl: qrCodeDataUrl,
|
qrCodeUrl: qrCodeDataUrl,
|
||||||
|
paymentUrl: mockPaymentUrl,
|
||||||
prepayId: `WECHAT_${Date.now()}`,
|
prepayId: `WECHAT_${Date.now()}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,11 +87,13 @@ export class PaymentService {
|
||||||
case PaymentMethod.ALIPAY:
|
case PaymentMethod.ALIPAY:
|
||||||
const alipayResult = await this.alipayAdapter.createPayment(order);
|
const alipayResult = await this.alipayAdapter.createPayment(order);
|
||||||
qrCodeUrl = alipayResult.qrCodeUrl;
|
qrCodeUrl = alipayResult.qrCodeUrl;
|
||||||
|
paymentUrl = alipayResult.paymentUrl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PaymentMethod.WECHAT:
|
case PaymentMethod.WECHAT:
|
||||||
const wechatResult = await this.wechatPayAdapter.createPayment(order);
|
const wechatResult = await this.wechatPayAdapter.createPayment(order);
|
||||||
qrCodeUrl = wechatResult.qrCodeUrl;
|
qrCodeUrl = wechatResult.qrCodeUrl;
|
||||||
|
paymentUrl = wechatResult.paymentUrl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PaymentMethod.CREDIT_CARD:
|
case PaymentMethod.CREDIT_CARD:
|
||||||
|
|
|
||||||
|
|
@ -220,19 +220,10 @@ function ToolCallResult({
|
||||||
<div className="mt-3 p-4 bg-white rounded-lg border border-secondary-200">
|
<div className="mt-3 p-4 bg-white rounded-lg border border-secondary-200">
|
||||||
<p className="text-sm text-secondary-600 mb-3">{result.message}</p>
|
<p className="text-sm text-secondary-600 mb-3">{result.message}</p>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
{result.qrCodeUrl ? (
|
{result.paymentUrl ? (
|
||||||
<div className="p-3 bg-white rounded-lg border border-secondary-100">
|
<div className="p-3 bg-white rounded-lg border border-secondary-100">
|
||||||
<QRCodeSVG value={result.qrCodeUrl} size={160} level="M" />
|
<QRCodeSVG value={result.paymentUrl} size={160} level="M" />
|
||||||
</div>
|
</div>
|
||||||
) : result.paymentUrl ? (
|
|
||||||
<a
|
|
||||||
href={result.paymentUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
|
|
||||||
>
|
|
||||||
前往支付
|
|
||||||
</a>
|
|
||||||
) : null}
|
) : null}
|
||||||
<p className="mt-3 text-xl font-semibold text-primary-600">
|
<p className="mt-3 text-xl font-semibold text-primary-600">
|
||||||
¥{result.amount}
|
¥{result.amount}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue