'data'
This commit is contained in:
@@ -21,7 +21,11 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
try {
|
||||
const { status, data } = await getSalonApi(request, `/api/salon/join/by-wechat?wechat_id=${encodeURIComponent(wechatId)}`);
|
||||
const { status, data } = await getSalonApi(
|
||||
request,
|
||||
`/api/salon/join/by-wechat?wechat_id=${encodeURIComponent(wechatId)}`,
|
||||
{ timeoutMs: 8000 }
|
||||
);
|
||||
if (status === 200 && data && typeof data === "object") {
|
||||
return NextResponse.json(data);
|
||||
}
|
||||
@@ -40,13 +44,16 @@ export async function GET(request: NextRequest) {
|
||||
const filter = encodeURIComponent(`wechatId = "${escaped}"`);
|
||||
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 8000);
|
||||
const res = await fetch(
|
||||
`${base}/api/collections/${pb.joinCollection}/records?filter=${filter}&sort=-created&perPage=1`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
cache: "no-store",
|
||||
signal: controller.signal,
|
||||
}
|
||||
);
|
||||
).finally(() => clearTimeout(timeoutId));
|
||||
if (!res.ok) return NextResponse.json(empty);
|
||||
const data = await res.json();
|
||||
const items = Array.isArray(data?.items) ? data.items : [];
|
||||
|
||||
@@ -46,6 +46,7 @@ export async function POST(request: NextRequest) {
|
||||
const region = String(formData.region || "").trim();
|
||||
const availableTime = String(formData.available_time || "").trim();
|
||||
const isVolunteer = !!formData.is_volunteer;
|
||||
const qualify = !!formData.qualify; // 女性且小红书帖子>10 时自动通过审核
|
||||
/** reason 仅存自我介绍,不合并其他字段 */
|
||||
const contact = wechatOrPhone || wechat || phone;
|
||||
if (!nickname || !session || !agreeRules) {
|
||||
@@ -85,6 +86,7 @@ export async function POST(request: NextRequest) {
|
||||
age_range: ageRangeForRecord,
|
||||
first_time: firstTime,
|
||||
is_volunteer: isVolunteer,
|
||||
qualify,
|
||||
};
|
||||
if (phone.trim()) {
|
||||
record.phone = phone.trim();
|
||||
@@ -133,6 +135,7 @@ async function tryPocketBaseDirect(
|
||||
const base = pb.url.replace(/\/$/, "");
|
||||
const url = `${base}/api/collections/${pb.joinCollection}/records`;
|
||||
|
||||
const qualify = !!(record.qualify ?? false);
|
||||
const body: Record<string, unknown> = {
|
||||
user_id: record.user_id,
|
||||
nickname: record.nickname,
|
||||
@@ -149,6 +152,7 @@ async function tryPocketBaseDirect(
|
||||
amount: record.amount ?? 0,
|
||||
xiaohongshu_url: record.xiaohongshu_url ?? "",
|
||||
is_volunteer: record.is_volunteer ?? false,
|
||||
is_approved: qualify,
|
||||
age_range: record.age_range ?? "",
|
||||
first_time: record.first_time ?? "",
|
||||
};
|
||||
|
||||
@@ -1,19 +1,33 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getAdminToken } from "@/app/lib/pocketbase/admin";
|
||||
import { getPocketBaseConfig } from "@/config/services.config";
|
||||
import { getSalonApi } from "@/app/lib/salonApi";
|
||||
|
||||
/** 获取志愿者列表 - 优先 salonapi,回退直连 PocketBase */
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { status, data } = await getSalonApi(request, "/api/salon/join/volunteers", {
|
||||
timeoutMs: 8000,
|
||||
});
|
||||
if (status === 200 && data && typeof data === "object") {
|
||||
const v = (data as { volunteer?: unknown }).volunteer;
|
||||
const payload = v && typeof v === "object" ? { volunteer: v } : { volunteer: null };
|
||||
return NextResponse.json(payload);
|
||||
}
|
||||
} catch {
|
||||
// salonapi 不可用,回退直连 PocketBase
|
||||
}
|
||||
|
||||
/** 获取志愿者列表 - solanRed 中 is_volunteer=true 的记录 */
|
||||
export async function GET() {
|
||||
const token = await getAdminToken();
|
||||
if (!token) {
|
||||
return NextResponse.json({ ok: true, volunteers: [] }, { status: 200 });
|
||||
return NextResponse.json({ ok: true, volunteer: null }, { status: 200 });
|
||||
}
|
||||
|
||||
const pb = getPocketBaseConfig();
|
||||
const base = pb.url.replace(/\/$/, "");
|
||||
|
||||
try {
|
||||
const filter = encodeURIComponent('is_volunteer=true && is_approved=true');
|
||||
const filter = encodeURIComponent("is_volunteer = true && is_approved = true");
|
||||
const res = await fetch(
|
||||
`${base}/api/collections/${pb.joinCollection}/records?filter=${filter}&sort=-created&perPage=20`,
|
||||
{
|
||||
@@ -22,19 +36,20 @@ export async function GET() {
|
||||
}
|
||||
);
|
||||
if (!res.ok) {
|
||||
return NextResponse.json({ ok: true, volunteers: [] }, { status: 200 });
|
||||
return NextResponse.json({ ok: true, volunteer: null }, { status: 200 });
|
||||
}
|
||||
const data = await res.json();
|
||||
const items = Array.isArray(data?.items) ? data.items : [];
|
||||
const volunteers = items.map(
|
||||
(r: { nickname?: string; xiaohongshu_nickname?: string; media?: string; xiaohongshu_url?: string }) => ({
|
||||
nickname: String(r?.xiaohongshu_nickname || r?.nickname || "").trim() || "志愿者",
|
||||
avatar: String(r?.media || "").trim() || undefined,
|
||||
xiaohongshu_url: String(r?.xiaohongshu_url || "").trim() || undefined,
|
||||
})
|
||||
);
|
||||
return NextResponse.json({ ok: true, volunteers });
|
||||
const rec = items[0] ?? null;
|
||||
const volunteer = rec
|
||||
? {
|
||||
nickname: String(rec?.xiaohongshu_nickname || rec?.nickname || "").trim() || "志愿者",
|
||||
avatar: String(rec?.media || "").trim() || undefined,
|
||||
xiaohongshu_url: String(rec?.xiaohongshu_url || "").trim() || undefined,
|
||||
}
|
||||
: null;
|
||||
return NextResponse.json({ ok: true, volunteer });
|
||||
} catch {
|
||||
return NextResponse.json({ ok: true, volunteers: [] }, { status: 200 });
|
||||
return NextResponse.json({ ok: true, volunteer: null }, { status: 200 });
|
||||
}
|
||||
}
|
||||
|
||||
151
app/api/pay/complete-order/route.ts
Normal file
151
app/api/pay/complete-order/route.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 本地直接确认支付:检测到 paid 后立即写入 PocketBase,不依赖 salonapi
|
||||
* 用于扫码页 doConfirm,避免「检测到支付,正在确认…」等待过久
|
||||
*/
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getAdminToken } from "@/app/lib/pocketbase/admin";
|
||||
import { getPocketBaseConfig, SITE_ID } from "@/config/services.config";
|
||||
import { queryZPayOrderStatus } from "@/app/lib/zpayStatus";
|
||||
import { queryXorPayOrderStatus } from "@/app/lib/xorpayStatus";
|
||||
|
||||
function parseOrderId(orderId: string): { userId: string; payType: string } {
|
||||
const id = (orderId || "").trim();
|
||||
if (!id.includes("_order_")) return { userId: "", payType: "unknown" };
|
||||
const prefix = id.split("_order_")[0];
|
||||
const lastUnderscore = prefix.lastIndexOf("_");
|
||||
if (lastUnderscore > 0) {
|
||||
return {
|
||||
userId: prefix.substring(0, lastUnderscore),
|
||||
payType: prefix.substring(lastUnderscore + 1).toLowerCase(),
|
||||
};
|
||||
}
|
||||
return { userId: prefix, payType: "unknown" };
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json().catch(() => ({}));
|
||||
const orderId = String(body?.order_id || "").trim();
|
||||
if (!orderId) {
|
||||
return NextResponse.json({ ok: false, error: "缺少 order_id" }, { status: 400 });
|
||||
}
|
||||
|
||||
const { userId, payType } = parseOrderId(orderId);
|
||||
if (!userId || payType !== "salon") {
|
||||
return NextResponse.json({ ok: false, error: "订单格式无效" }, { status: 400 });
|
||||
}
|
||||
|
||||
const [zpay, xorpay] = await Promise.all([
|
||||
queryZPayOrderStatus(orderId, 5000),
|
||||
queryXorPayOrderStatus(orderId, 5000),
|
||||
]);
|
||||
|
||||
const paid = zpay?.paid || xorpay?.paid;
|
||||
if (!paid) {
|
||||
return NextResponse.json({ ok: false, error: "订单未支付" }, { status: 400 });
|
||||
}
|
||||
|
||||
const moneyYuan = parseFloat(zpay?.money || xorpay?.payPrice || "0") || 0;
|
||||
const amountCents = moneyYuan > 0 ? Math.round(moneyYuan * 100) : 100;
|
||||
|
||||
const token = await getAdminToken();
|
||||
if (!token) {
|
||||
return NextResponse.json({ ok: false, error: "PocketBase 未配置" }, { status: 503 });
|
||||
}
|
||||
|
||||
const pb = getPocketBaseConfig();
|
||||
const base = pb.url.replace(/\/$/, "");
|
||||
|
||||
const escapedUserId = userId.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
||||
const filter = encodeURIComponent(`user_id = "${escapedUserId}"`);
|
||||
|
||||
const listRes = await fetch(
|
||||
`${base}/api/collections/${pb.joinCollection}/records?filter=${filter}&sort=-created&perPage=1`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
cache: "no-store",
|
||||
}
|
||||
);
|
||||
|
||||
if (!listRes.ok) {
|
||||
return NextResponse.json({ ok: false, error: "查询记录失败" }, { status: 500 });
|
||||
}
|
||||
|
||||
const listData = await listRes.json().catch(() => ({}));
|
||||
const items = Array.isArray(listData?.items) ? listData.items : [];
|
||||
const rec = items[0] ?? null;
|
||||
|
||||
if (rec?.id) {
|
||||
const patchRes = await fetch(
|
||||
`${base}/api/collections/${pb.joinCollection}/records/${rec.id}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
order_id: orderId,
|
||||
amount: amountCents,
|
||||
vip: true,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!patchRes.ok) {
|
||||
const err = await patchRes.json().catch(() => ({}));
|
||||
console.warn("[pay/complete-order] solanRed patch failed:", patchRes.status, err);
|
||||
}
|
||||
}
|
||||
|
||||
const siteVipFilter = encodeURIComponent(
|
||||
`user_id = "${escapedUserId}" && site_id = "${SITE_ID}"`
|
||||
);
|
||||
const siteVipList = await fetch(
|
||||
`${base}/api/collections/site_vip/records?filter=${siteVipFilter}&perPage=1`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
cache: "no-store",
|
||||
}
|
||||
);
|
||||
|
||||
if (siteVipList.ok) {
|
||||
const svData = await siteVipList.json().catch(() => ({}));
|
||||
const svItems = svData?.items ?? [];
|
||||
if (svItems.length > 0) {
|
||||
await fetch(
|
||||
`${base}/api/collections/site_vip/records/${svItems[0].id}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ order_id: orderId }),
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await fetch(`${base}/api/collections/site_vip/records`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userId,
|
||||
site_id: SITE_ID,
|
||||
order_id: orderId,
|
||||
expires_at: "",
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok: true });
|
||||
} catch (error) {
|
||||
console.error("[pay/complete-order] error:", error);
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: error instanceof Error ? error.message : "操作失败" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -66,14 +66,13 @@ function buildQrHtml(
|
||||
imgSrc: string,
|
||||
returnUrl: string,
|
||||
orderId: string,
|
||||
opts?: { channel?: string; successDesc?: string; confirmUrl?: string }
|
||||
opts?: { channel?: string; confirmUrl?: string }
|
||||
): string {
|
||||
const ch = (opts?.channel || "wxpay").toLowerCase();
|
||||
const isWx = ch === "wxpay" || ch === "wx" || ch === "wechat";
|
||||
const title = isWx ? "微信扫码支付" : "支付宝扫码支付";
|
||||
const hint = isWx ? "请使用微信扫描下方二维码完成支付" : "请使用支付宝扫描下方二维码完成支付";
|
||||
const successDesc = opts?.successDesc || "支付成功,即将跳转";
|
||||
const confirmUrl = opts?.confirmUrl || "/api/salon/complete-order";
|
||||
const confirmUrl = opts?.confirmUrl || "/api/pay/complete-order";
|
||||
const esc = (s: string) =>
|
||||
s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c] || c);
|
||||
return `<!DOCTYPE html>
|
||||
@@ -96,14 +95,6 @@ h2{color:#1e293b;margin:0 0 8px;font-size:1.5rem;}
|
||||
.countdown-num{font-size:1rem;font-weight:700;color:#1e293b;line-height:1;}
|
||||
.countdown-unit{font-size:9px;color:#94a3b8;}
|
||||
.tip{font-size:13px;color:#64748b;margin-top:16px;}
|
||||
.success-wrap{display:none;}
|
||||
.success-wrap.show{display:block;}
|
||||
.wait-wrap.hide{display:none;}
|
||||
.success-icon{font-size:4rem;margin-bottom:16px;animation:scaleIn .4s ease;}
|
||||
.success-title{font-size:1.25rem;color:#16a34a;font-weight:600;margin-bottom:8px;}
|
||||
.success-desc{color:#64748b;font-size:14px;margin-bottom:24px;}
|
||||
.success-countdown{font-size:1.5rem;font-weight:700;color:#667eea;}
|
||||
@keyframes scaleIn{from{transform:scale(0);opacity:0;}to{transform:scale(1);opacity:1;}}
|
||||
@keyframes pulse{0%,100%{opacity:1;}50%{opacity:.7;}}
|
||||
.pulse{animation:pulse 2s ease-in-out infinite;}
|
||||
</style></head>
|
||||
@@ -129,12 +120,6 @@ h2{color:#1e293b;margin:0 0 8px;font-size:1.5rem;}
|
||||
</div>
|
||||
<p class="tip pulse" id="tip">等待支付中...</p>
|
||||
</div>
|
||||
<div class="success-wrap" id="successWrap">
|
||||
<div class="success-icon">✅</div>
|
||||
<div class="success-title">支付成功</div>
|
||||
<div class="success-desc">${esc(successDesc)}</div>
|
||||
<div class="success-countdown" id="successCountdown">3 秒后跳转</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
@@ -143,11 +128,9 @@ h2{color:#1e293b;margin:0 0 8px;font-size:1.5rem;}
|
||||
var returnUrl=${JSON.stringify(returnUrl)};
|
||||
if(!orderId){return;}
|
||||
var waitWrap=document.getElementById("waitWrap");
|
||||
var successWrap=document.getElementById("successWrap");
|
||||
var tip=document.getElementById("tip");
|
||||
var countdownNum=document.getElementById("countdownNum");
|
||||
var progressCircle=document.getElementById("progressCircle");
|
||||
var successCountdown=document.getElementById("successCountdown");
|
||||
var maxT=300,left=maxT;
|
||||
var circumference=2*Math.PI*32;
|
||||
function fmt(t){var m=Math.floor(t/60),s=t%60;return m+":"+(s<10?"0":"")+s;}
|
||||
@@ -159,36 +142,46 @@ h2{color:#1e293b;margin:0 0 8px;font-size:1.5rem;}
|
||||
},1000);
|
||||
function showSuccess(){
|
||||
clearInterval(countdownTimer);
|
||||
waitWrap.classList.add("hide");
|
||||
successWrap.classList.add("show");
|
||||
var s=3;
|
||||
var t=setInterval(function(){
|
||||
s--;
|
||||
successCountdown.textContent=s>0?s+" 秒后跳转":"跳转中…";
|
||||
if(s<=0){clearInterval(t);window.location.href=returnUrl;}
|
||||
},1000);
|
||||
window.location.href=returnUrl;
|
||||
}
|
||||
var confirmUrl=${JSON.stringify(confirmUrl)};
|
||||
var confirmTimeoutMs=8000;
|
||||
function doConfirm(attempt){
|
||||
if(attempt>=3){showSuccess();return;}
|
||||
fetch(confirmUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({order_id:orderId})})
|
||||
if(tip){tip.textContent="检测到支付,正在确认…";}
|
||||
var ctrl=new AbortController();
|
||||
var tid=setTimeout(function(){ctrl.abort();},confirmTimeoutMs);
|
||||
fetch(confirmUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({order_id:orderId}),signal:ctrl.signal})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(cd){
|
||||
clearTimeout(tid);
|
||||
if(cd&&cd.ok){showSuccess();}
|
||||
else{setTimeout(function(){doConfirm(attempt+1);},800);}
|
||||
})
|
||||
.catch(function(){setTimeout(function(){doConfirm(attempt+1);},800);});
|
||||
.catch(function(){clearTimeout(tid);if(tip){tip.textContent="确认失败,重试中…";}setTimeout(function(){doConfirm(attempt+1);},800);});
|
||||
}
|
||||
function check(){
|
||||
fetch("/api/pay/status?order_id="+encodeURIComponent(orderId),{cache:"no-store"})
|
||||
fetch("/api/pay/status?order_id="+encodeURIComponent(orderId),{cache:"no-store",credentials:"same-origin"})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(d){
|
||||
if(d.ok&&d.paid){doConfirm(0);return;}
|
||||
setTimeout(check,500);
|
||||
if(d&&d.paid){doConfirm(0);return;}
|
||||
setTimeout(check,300);
|
||||
})
|
||||
.catch(function(){setTimeout(check,500);});
|
||||
.catch(function(){setTimeout(check,1000);});
|
||||
}
|
||||
setTimeout(check,500);
|
||||
var manualBtn=document.createElement("button");
|
||||
manualBtn.textContent="支付完成?点此手动检查";
|
||||
manualBtn.style.cssText="margin-top:12px;padding:8px 16px;font-size:13px;color:#64748b;background:#f1f5f9;border:none;border-radius:8px;cursor:pointer";
|
||||
manualBtn.onclick=function(){
|
||||
manualBtn.disabled=true;
|
||||
manualBtn.textContent="检查中…";
|
||||
fetch("/api/pay/status?order_id="+encodeURIComponent(orderId),{cache:"no-store",credentials:"same-origin"})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(d){if(d&&d.paid){doConfirm(0);}else{manualBtn.disabled=false;manualBtn.textContent="支付完成?点此手动检查";}})
|
||||
.catch(function(){manualBtn.disabled=false;manualBtn.textContent="检查失败,重试";});
|
||||
};
|
||||
if(tip&&tip.parentNode){tip.parentNode.appendChild(manualBtn);}
|
||||
setTimeout(check,300);
|
||||
})();
|
||||
</script>
|
||||
</body></html>`;
|
||||
@@ -401,8 +394,7 @@ export async function POST(request: NextRequest) {
|
||||
const rawOrderId = String(resData.order_id || created.orderId).trim();
|
||||
const html = buildQrHtml(imgSrc, safeReturnUrl, rawOrderId, {
|
||||
channel: resData.channel || "wxpay",
|
||||
successDesc: "支付成功,申请已提交",
|
||||
confirmUrl: "/api/salon/complete-order",
|
||||
confirmUrl: "/api/pay/complete-order",
|
||||
});
|
||||
const response = new NextResponse(html, {
|
||||
status: 200,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getApiUrlFromRequest, getPaymentConfig } from "@/config/services.config";
|
||||
import { queryXorPayOrderStatus } from "@/app/lib/xorpayStatus";
|
||||
import { queryZPayOrderStatus } from "@/app/lib/zpayStatus";
|
||||
|
||||
function resolvePayApiUrl(request: NextRequest): string {
|
||||
const config = getPaymentConfig();
|
||||
@@ -8,6 +10,10 @@ function resolvePayApiUrl(request: NextRequest): string {
|
||||
return (getApiUrlFromRequest(hostHeader) || config.apiUrl).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付状态查询:salonapi 优先,失败时直接向 zpay/xorpay 查询兜底
|
||||
* 参考 nomadvip,保证本地无回调时也能检测到支付
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const orderId = request.nextUrl.searchParams.get("order_id");
|
||||
if (!orderId) {
|
||||
@@ -20,27 +26,36 @@ export async function GET(request: NextRequest) {
|
||||
const hostHeader =
|
||||
request.headers.get("host") || request.headers.get("x-forwarded-host");
|
||||
const apiUrl = resolvePayApiUrl(request);
|
||||
const url = `${apiUrl}/salon/order_status?order_id=${encodeURIComponent(orderId)}`;
|
||||
const salonUrl = `${apiUrl}/salon/order_status?order_id=${encodeURIComponent(orderId)}`;
|
||||
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 3000);
|
||||
const res = await fetch(url, {
|
||||
cache: "no-store",
|
||||
signal: controller.signal,
|
||||
}).finally(() => clearTimeout(timeout));
|
||||
const data = await res.json().catch(() => ({}));
|
||||
const paid = !!data?.paid;
|
||||
if (paid) {
|
||||
return NextResponse.json({ ok: true, paid: true });
|
||||
// 并行查询 salonapi、xorpay、zpay,任一返回 paid 即立即返回,避免串行阻塞
|
||||
const salonPaid = (async () => {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 3000);
|
||||
const res = await fetch(salonUrl, {
|
||||
cache: "no-store",
|
||||
signal: controller.signal,
|
||||
}).finally(() => clearTimeout(timeout));
|
||||
const data = await res.json().catch(() => ({}));
|
||||
return !!data?.paid;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[pay/status] salonapi order_id=%s url=%s error=%s",
|
||||
orderId,
|
||||
salonUrl,
|
||||
error instanceof Error ? error.message : String(error)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"[pay/status] error order_id=%s error=%s",
|
||||
orderId,
|
||||
error instanceof Error ? error.message : String(error)
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
const xorpayPaid = queryXorPayOrderStatus(orderId, 5000).then((r) => r?.paid ?? false);
|
||||
const zpayPaid = queryZPayOrderStatus(orderId, 5000).then((r) => r?.paid ?? false);
|
||||
|
||||
const [salon, xorpay, zpay] = await Promise.all([salonPaid, xorpayPaid, zpayPaid]);
|
||||
if (salon || xorpay || zpay) {
|
||||
return NextResponse.json({ ok: true, paid: true });
|
||||
}
|
||||
return NextResponse.json({ ok: true, paid: false });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user