Payment from the Android app: reach Zarinpal through pay.html so it carries our Referer
The app hands every outside URL to the phone browser, which sends no Referer, and Zarinpal's StartPay refuses a request without our domain as Referer («دسترسی از این دامنه مجاز نمیباشد»). Checked against production on a pending order: no Referer → refused; Referer https://math.tadrisino2.ir/ → forwards to the bank (sep.shaparak.ir). - pay.html + src/pay.ts: opened in the phone browser, forwards only to our own /api/payment/orders/<id>/pay link (anything else is refused), so the Referer is math.tadrisino2.ir. The app opens it as http:// (the WebView keeps only https on its own host); ArvanCloud moves it to https, and the link rides in the fragment, which never reaches a server. - MainActivity.kt (next APK): payment pages (our subdomains, zarinpal, shaparak) stay inside the WebView and `paysInApp()` tells the frontend to navigate directly. Not built. Not checked: a full payment on a real phone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -333,9 +333,18 @@ function CheckoutSheet({ plan, bazaar, onClose, onPaid }: { plan: Plan | null; b
|
||||
if (!order.paymentUrl) throw new Error('order without paymentUrl');
|
||||
// فقط http(s): یک «javascript:» یا «data:» از پاسخِ API نباید با assign اجرا شود.
|
||||
if (!/^https?:\/\//i.test(order.paymentUrl)) throw new Error('paymentUrl is not http(s)');
|
||||
window.location.assign(order.paymentUrl);
|
||||
// داخلِ اپ آدرسِ درگاه در مرورگرِ گوشی باز میشود و این صفحه همینجا میماند؛ دکمه نباید تا ابد بچرخد.
|
||||
if (bazaar) setTimeout(() => { setBusy(false); setDone('gateway'); }, 1500);
|
||||
const shell = (window as unknown as { TadrisinoAndroid?: { paysInApp?: () => boolean } }).TadrisinoAndroid;
|
||||
if (bazaar && !shell?.paysInApp?.()) {
|
||||
// اپِ اندرویدِ فعلی هر آدرسِ بیرونی را در مرورگرِ گوشی باز میکند و آن مرورگر Referer نمیفرستد؛ زرینپال
|
||||
// بیReferer میگوید «دسترسی از این دامنه مجاز نمیباشد». پس اول pay.htmlِ خودمان در مرورگر باز میشود
|
||||
// (src/pay.ts) و آن به درگاه میرود. «http://» عمداً است: WebView فقط httpsِ همین دامنه را در خودش نگه
|
||||
// میدارد؛ آروان بلافاصله به https میبرد و لینک در fragment است، پس به سرور نمیرسد.
|
||||
window.location.assign(`http://${window.location.host}/pay.html#u=${encodeURIComponent(order.paymentUrl)}`);
|
||||
setTimeout(() => { setBusy(false); setDone('gateway'); }, 1500);
|
||||
} else {
|
||||
// مرورگر، یا اپی که پرداخت را در خودش باز میکند (paysInApp): همین صفحه به درگاه میرود و Referer دارد.
|
||||
window.location.assign(order.paymentUrl);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(friendlyError(err, 'اتصال به درگاه انجام نشد، دوباره امتحان کن'));
|
||||
setBusy(false);
|
||||
@@ -397,7 +406,7 @@ function CheckoutSheet({ plan, bazaar, onClose, onPaid }: { plan: Plan | null; b
|
||||
<PrimaryButton loading={busy} onClick={() => void pay()}>پرداخت</PrimaryButton>
|
||||
{method === 'gateway' && (
|
||||
<p className="mt-3 flex items-center justify-center gap-1.5 text-xs text-muted">
|
||||
<ShieldCheck size={14} /> {bazaar ? 'صفحهی پرداخت در مرورگرِ گوشی باز میشه؛ بعدش برگرد به اپ.' : 'پرداخت در درگاهِ امنِ زرینپال انجام میشه.'}
|
||||
<ShieldCheck size={14} /> پرداخت در درگاهِ امنِ زرینپال انجام میشه.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user