feat(payment): 虎皮椒聚合支付 - 官方插件与跨平台可执行文件
新增 xunhupay 官方包,支付 RPC 与宿主只保留订单和验签边界;官方支付包同时带上各系统 provider,避免把 Linux ELF 当成本机入口。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
23
web/src/components/payment-checkout-code.css
Normal file
23
web/src/components/payment-checkout-code.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.payment-checkout-code {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 232px;
|
||||
height: 232px;
|
||||
padding: 12px;
|
||||
border-radius: 16px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.payment-checkout-code img,
|
||||
.payment-checkout-code canvas,
|
||||
.payment-checkout-code svg {
|
||||
display: block;
|
||||
width: 208px;
|
||||
height: 208px;
|
||||
}
|
||||
|
||||
.payment-checkout-code img {
|
||||
object-fit: contain;
|
||||
background: #ffffff;
|
||||
}
|
||||
24
web/src/components/payment-checkout-code.tsx
Normal file
24
web/src/components/payment-checkout-code.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { QRCode } from "antd";
|
||||
|
||||
import { isPaymentQrImageURL } from "@/lib/payment-checkout-code";
|
||||
|
||||
import "./payment-checkout-code.css";
|
||||
|
||||
const QR_SIZE = 208;
|
||||
const QR_DARK = "#111111";
|
||||
const QR_LIGHT = "#ffffff";
|
||||
|
||||
export function PaymentCheckoutCode({ value }: { value: string }) {
|
||||
if (isPaymentQrImageURL(value)) {
|
||||
return (
|
||||
<div className="payment-checkout-code">
|
||||
<img src={value} alt="支付二维码" width={QR_SIZE} height={QR_SIZE} referrerPolicy="no-referrer" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="payment-checkout-code">
|
||||
<QRCode value={value} size={QR_SIZE} bordered={false} color={QR_DARK} bgColor={QR_LIGHT} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
19
web/src/lib/payment-checkout-code.test.ts
Normal file
19
web/src/lib/payment-checkout-code.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { isPaymentQrImageURL } from "./payment-checkout-code";
|
||||
|
||||
test("isPaymentQrImageURL 识别虎皮椒二维码图片地址", () => {
|
||||
assert.equal(isPaymentQrImageURL("https://api.xunhupay.com/qrcode.png"), true);
|
||||
assert.equal(isPaymentQrImageURL(" https://api.dpweixin.com/qrcode.png "), true);
|
||||
assert.equal(isPaymentQrImageURL("https://pay.example/qrcode"), true);
|
||||
assert.equal(isPaymentQrImageURL("http://pay.example/images/order-qr.jpg"), true);
|
||||
});
|
||||
|
||||
test("isPaymentQrImageURL 不把待编码载荷或收银页当成图片", () => {
|
||||
assert.equal(isPaymentQrImageURL("weixin://wxpay/bizpayurl?pr=abc"), false);
|
||||
assert.equal(isPaymentQrImageURL("https://api.xunhupay.com/pay/jump"), false);
|
||||
assert.equal(isPaymentQrImageURL("https://pay.example/checkout?qrcode=1"), false);
|
||||
assert.equal(isPaymentQrImageURL(""), false);
|
||||
assert.equal(isPaymentQrImageURL("/qrcode.png"), false);
|
||||
});
|
||||
20
web/src/lib/payment-checkout-code.ts
Normal file
20
web/src/lib/payment-checkout-code.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const IMAGE_PATH = /\.(?:png|jpe?g|gif|webp|bmp|svg)$/i;
|
||||
const QRCODE_PATH = /(?:^|\/)qrcode(?:[-_./]|$)/i;
|
||||
|
||||
/** 渠道已生成的二维码图片地址,应直接展示,不能再编码成新的二维码。 */
|
||||
export function isPaymentQrImageURL(value: string): boolean {
|
||||
const raw = value.trim();
|
||||
if (!raw) {
|
||||
return false;
|
||||
}
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(raw);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
||||
return false;
|
||||
}
|
||||
return IMAGE_PATH.test(parsed.pathname) || QRCODE_PATH.test(parsed.pathname);
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 1. 包格式
|
||||
|
||||
上传文件必须是 `.yingce-plugin` ZIP 包,大小不超过 16 MiB。包内必须有根目录 `manifest.json`;不能上传裸 JSON。清单和可选的 Web 运行时代码、静态资源属于同一个版本、权限和生命周期:
|
||||
上传文件必须是 `.yingce-plugin` ZIP 包,大小不超过 48 MiB。包内必须有根目录 `manifest.json`;不能上传裸 JSON。清单和可选的 Web 运行时代码、静态资源属于同一个版本、权限和生命周期:
|
||||
|
||||
```text
|
||||
my-plugin.yingce-plugin
|
||||
|
||||
@@ -82,7 +82,7 @@ export function UploadPluginModal({ open, onClose, onUpload }: UploadPluginModal
|
||||
>
|
||||
<CloudUpload className="plugin-upload-dropzone-icon" />
|
||||
<p className="ant-upload-text">{isDraggingPlugin ? "释放文件以上传插件" : "点击选择插件文件,也可拖拽到此处"}</p>
|
||||
<p className="ant-upload-hint">支持 .yingce-plugin 包 · 大小不超过 16 MiB</p>
|
||||
<p className="ant-upload-hint">支持 .yingce-plugin 包 · 大小不超过 48 MiB</p>
|
||||
</Upload.Dragger>
|
||||
</div>
|
||||
<div className="plugin-upload-notice">
|
||||
|
||||
Reference in New Issue
Block a user