เราสร้างระบบการชำระเงินที่แข็งแกร่งด้วย Stripe และ PayPal ได้อย่างไร: แนวทาง Trifecta

คำนำ
ที่ Forward Email เราให้ความสำคัญกับการสร้างระบบที่เชื่อถือได้ แม่นยำ และใช้งานง่ายมาโดยตลอด เมื่อพูดถึงการนำระบบประมวลผลการชำระเงินของเราไปใช้ เรารู้ดีว่าเราต้องการโซลูชันที่สามารถรองรับการประมวลผลการชำระเงินหลายระบบได้ พร้อมกับรักษาความสอดคล้องของข้อมูลอย่างสมบูรณ์แบบ บล็อกโพสต์นี้อธิบายรายละเอียดว่าทีมพัฒนาของเราผสานรวม Stripe และ PayPal เข้าด้วยกันอย่างไร โดยใช้แนวทาง trifecta ที่รับประกันความแม่นยำแบบเรียลไทม์แบบ 1:1 ทั่วทั้งระบบของเรา
ความท้าทาย: ผู้ประมวลผลการชำระเงินหลายราย แหล่งข้อมูลความจริงเพียงแหล่งเดียว
ในฐานะบริการอีเมลที่เน้นความเป็นส่วนตัว เราต้องการมอบทางเลือกในการชำระเงินให้กับผู้ใช้ บางคนชอบความเรียบง่ายของการชำระเงินด้วยบัตรเครดิตผ่าน Stripe ขณะที่บางคนให้ความสำคัญกับการแยกส่วนเพิ่มเติมที่ PayPal มอบให้ อย่างไรก็ตาม การรองรับระบบประมวลผลการชำระเงินหลายระบบทำให้เกิดความซับซ้อนอย่างมาก:
- เราจะมั่นใจได้อย่างไรว่าข้อมูลมีความสอดคล้องกันในระบบการชำระเงินที่แตกต่างกัน
- เราจะจัดการกับกรณีพิเศษ เช่น ข้อโต้แย้ง การคืนเงิน หรือการชำระเงินที่ล้มเหลวได้อย่างไร
- เราจะรักษาแหล่งข้อมูลที่เชื่อถือได้เพียงหนึ่งเดียวในฐานข้อมูลของเราได้อย่างไร
โซลูชันของเราคือการนำสิ่งที่เราเรียกว่า "แนวทาง trifecta" มาใช้ ซึ่งเป็นระบบสามชั้นที่ให้ความซ้ำซ้อนและรับรองความสอดคล้องของข้อมูลไม่ว่าจะเกิดอะไรขึ้นก็ตาม
แนวทาง Trifecta: ความน่าเชื่อถือสามชั้น
ระบบการชำระเงินของเรามีส่วนประกอบสำคัญสามประการที่ทำงานร่วมกันเพื่อให้แน่ใจว่าข้อมูลมีการซิงโครไนซ์กันอย่างสมบูรณ์แบบ:
- การเปลี่ยนเส้นทางหลังการชำระเงิน - รวบรวมข้อมูลการชำระเงินทันทีหลังการชำระเงิน
- ตัวจัดการเว็บฮุก - ประมวลผลเหตุการณ์แบบเรียลไทม์จากผู้ประมวลผลการชำระเงิน
- งานอัตโนมัติ - ตรวจสอบและกระทบยอดข้อมูลการชำระเงินเป็นระยะ
มาเจาะลึกแต่ละส่วนประกอบและดูว่าพวกมันทำงานร่วมกันอย่างไร
เลเยอร์ 1: การเปลี่ยนเส้นทางหลังการชำระเงิน
ขั้นตอนแรกของวิธีการ Trifecta ของเราจะเกิดขึ้นทันทีหลังจากที่ผู้ใช้ชำระเงินเสร็จสิ้น ทั้ง Stripe และ PayPal มีกลไกสำหรับนำผู้ใช้กลับมายังเว็บไซต์ของเราพร้อมข้อมูลธุรกรรม
การใช้งาน ### ของ Stripe Checkout
สำหรับ Stripe เราใช้ Checkout Sessions API เพื่อสร้างประสบการณ์การชำระเงินที่ราบรื่น เมื่อผู้ใช้เลือกแพ็กเกจและชำระเงินด้วยบัตรเครดิต เราจะสร้าง Checkout Session พร้อม URL เฉพาะสำหรับความสำเร็จและการยกเลิก:
const options = {
mode: paymentType === 'one-time' ? 'payment' : 'subscription',
customer: ctx.state.user[config.userFields.stripeCustomerID],
client_reference_id: reference,
metadata: {
plan
},
line_items: [
{
price,
quantity: 1,
description
}
],
locale: config.STRIPE_LOCALES.has(ctx.locale) ? ctx.locale : 'auto',
cancel_url: `${config.urls.web}${ctx.path}${
isMakePayment || isEnableAutoRenew ? '' : `/?plan=${plan}`
}`,
success_url: `${config.urls.web}${ctx.path}/?${
isMakePayment || isEnableAutoRenew ? '' : `plan=${plan}&`
}session_id={CHECKOUT_SESSION_ID}`,
allow_promotion_codes: true
};
// Create the checkout session and redirect
const session = await stripe.checkout.sessions.create(options);
const redirectTo = session.url;
if (ctx.accepts('html')) {
ctx.status = 303;
ctx.redirect(redirectTo);
} else {
ctx.body = { redirectTo };
}
ส่วนสำคัญที่นี่คือพารามิเตอร์ success_url
ซึ่งมี session_id
เป็นพารามิเตอร์การค้นหา เมื่อ Stripe เปลี่ยนเส้นทางผู้ใช้กลับมายังเว็บไซต์ของเราหลังจากชำระเงินสำเร็จ เราสามารถใช้รหัสเซสชันนี้เพื่อตรวจสอบธุรกรรมและอัปเดตฐานข้อมูลของเราให้เหมาะสม
ขั้นตอนการชำระเงิน PayPal
สำหรับ PayPal เราใช้แนวทางที่คล้ายกันกับ Orders API ของพวกเขา:
const requestBody = {
intent: 'CAPTURE',
application_context: {
cancel_url: `${config.urls.web}${ctx.path}${
isMakePayment || isEnableAutoRenew ? '' : `/?plan=${plan}`
}`,
return_url: `${config.urls.web}${ctx.path}/?plan=${plan}`,
brand_name: 'Forward Email',
shipping_preference: 'NO_SHIPPING',
user_action: 'PAY_NOW'
},
payer: {
email_address: ctx.state.user.email
},
purchase_units: [
{
reference_id: ctx.state.user.id,
description,
custom_id: sku,
invoice_id: reference,
soft_descriptor: sku,
amount: {
currency_code: 'USD',
value: price,
breakdown: {
item_total: {
currency_code: 'USD',
value: price
}
}
},
items: [
{
name,
description,
sku,
unit_amount: {
currency_code: 'USD',
value: price
},
quantity: '1',
category: 'DIGITAL_GOODS'
}
]
}
]
};
เช่นเดียวกับ Stripe เรากำหนดพารามิเตอร์ return_url
และ cancel_url
เพื่อจัดการการเปลี่ยนเส้นทางหลังการชำระเงิน เมื่อ PayPal เปลี่ยนเส้นทางผู้ใช้กลับมายังเว็บไซต์ของเรา เราจะสามารถบันทึกรายละเอียดการชำระเงินและอัปเดตฐานข้อมูลของเราได้
เลเยอร์ 2: ตัวจัดการ Webhook พร้อมการตรวจสอบลายเซ็น
แม้ว่าการเปลี่ยนเส้นทางหลังการชำระเงินจะใช้งานได้ดีในสถานการณ์ส่วนใหญ่ แต่ก็ไม่ได้หมายความว่าจะปลอดภัยเสมอไป ผู้ใช้อาจปิดเบราว์เซอร์ก่อนที่จะถูกเปลี่ยนเส้นทาง หรือปัญหาเครือข่ายอาจทำให้การเปลี่ยนเส้นทางไม่เสร็จสมบูรณ์ ซึ่งนี่แหละคือที่มาของเว็บฮุก
ทั้ง Stripe และ PayPal มีระบบเว็บฮุกที่ส่งการแจ้งเตือนแบบเรียลไทม์เกี่ยวกับเหตุการณ์การชำระเงิน เราได้ติดตั้งระบบจัดการเว็บฮุกที่มีประสิทธิภาพ ซึ่งตรวจสอบความถูกต้องของการแจ้งเตือนเหล่านี้และประมวลผลตามความเหมาะสม
การใช้งาน Stripe Webhook
ตัวจัดการเว็บฮุก Stripe ของเราจะตรวจสอบลายเซ็นของเหตุการณ์เว็บฮุกขาเข้าเพื่อให้แน่ใจว่าถูกต้องตามกฎหมาย:
async function webhook(ctx) {
const sig = ctx.request.get('stripe-signature');
// throw an error if something was wrong
if (!isSANB(sig))
throw Boom.badRequest(ctx.translateError('INVALID_STRIPE_SIGNATURE'));
const event = stripe.webhooks.constructEvent(
ctx.request.rawBody,
sig,
env.STRIPE_ENDPOINT_SECRET
);
// throw an error if something was wrong
if (!event)
throw Boom.badRequest(ctx.translateError('INVALID_STRIPE_SIGNATURE'));
ctx.logger.info('stripe webhook', { event });
// return a response to acknowledge receipt of the event
ctx.body = { received: true };
// run in background
processEvent(ctx, event)
.then()
.catch((err) => {
ctx.logger.fatal(err, { event });
// email admin errors
emailHelper({
template: 'alert',
message: {
to: config.email.message.from,
subject: `Error with Stripe Webhook (Event ID ${event.id})`
},
locals: {
message: `<pre><code>${safeStringify(
parseErr(err),
null,
2
)}</code></pre>`
}
})
.then()
.catch((err) => ctx.logger.fatal(err, { event }));
});
}
ฟังก์ชัน stripe.webhooks.constructEvent
จะตรวจสอบลายเซ็นโดยใช้รหัสลับปลายทางของเรา หากลายเซ็นถูกต้อง เราจะประมวลผลเหตุการณ์แบบอะซิงโครนัสเพื่อหลีกเลี่ยงการบล็อกการตอบสนองของเว็บฮุก
การใช้งาน PayPal Webhook
ในทำนองเดียวกัน ตัวจัดการเว็บฮุก PayPal ของเรายังตรวจสอบความถูกต้องของการแจ้งเตือนขาเข้า:
async function webhook(ctx) {
const response = await promisify(
paypal.notification.webhookEvent.verify,
paypal.notification.webhookEvent
)(ctx.request.headers, ctx.request.body, env.PAYPAL_WEBHOOK_ID);
// throw an error if something was wrong
if (!_.isObject(response) || response.verification_status !== 'SUCCESS')
throw Boom.badRequest(ctx.translateError('INVALID_PAYPAL_SIGNATURE'));
// return a response to acknowledge receipt of the event
ctx.body = { received: true };
// run in background
processEvent(ctx)
.then()
.catch((err) => {
ctx.logger.fatal(err);
// email admin errors
emailHelper({
template: 'alert',
message: {
to: config.email.message.from,
subject: `Error with PayPal Webhook (Event ID ${ctx.request.body.id})`
},
locals: {
message: `<pre><code>${safeStringify(
parseErr(err),
null,
2
)}</code></pre>`
}
})
.then()
.catch((err) => ctx.logger.fatal(err));
});
}
ตัวจัดการเว็บฮุกทั้งสองมีรูปแบบเดียวกัน คือ ตรวจสอบลายเซ็น ยืนยันการรับ และประมวลผลเหตุการณ์แบบอะซิงโครนัส วิธีนี้ช่วยให้มั่นใจได้ว่าเราจะไม่พลาดเหตุการณ์การชำระเงิน แม้ว่าการเปลี่ยนเส้นทางหลังการชำระเงินจะล้มเหลวก็ตาม
เลเยอร์ 3: งานอัตโนมัติด้วย Bree
เลเยอร์สุดท้ายของแนวทาง trifecta ของเราคือชุดงานอัตโนมัติที่ตรวจสอบและปรับยอดข้อมูลการชำระเงินเป็นระยะ เราใช้ Bree ซึ่งเป็นตัวกำหนดตารางงานสำหรับ Node.js เพื่อรันงานเหล่านี้อย่างสม่ำเสมอ
ตัวตรวจสอบความถูกต้องของการสมัครสมาชิก
งานสำคัญอย่างหนึ่งของเราคือการตรวจสอบความถูกต้องของการสมัครสมาชิก ซึ่งจะช่วยให้แน่ใจว่าฐานข้อมูลของเราสะท้อนสถานะการสมัครสมาชิกใน Stripe ได้อย่างถูกต้อง:
async function mapper(customer) {
// wait a second to prevent rate limitation error
await setTimeout(ms('1s'));
// check for user on our side
let user = await Users.findOne({
[config.userFields.stripeCustomerID]: customer.id
})
.lean()
.exec();
if (!user) return;
if (user.is_banned) return;
// if emails did not match
if (user.email !== customer.email) {
logger.info(
`User email ${user.email} did not match customer email ${customer.email} (${customer.id})`
);
customer = await stripe.customers.update(customer.id, {
email: user.email
});
logger.info(`Updated user email to match ${user.email}`);
}
// check for active subscriptions
const [activeSubscriptions, trialingSubscriptions] = await Promise.all([
stripe.subscriptions.list({
customer: customer.id,
status: 'active'
}),
stripe.subscriptions.list({
customer: customer.id,
status: 'trialing'
})
]);
// Combine active and trialing subscriptions
let subscriptions = [
...activeSubscriptions.data,
...trialingSubscriptions.data
];
// Handle edge case: multiple subscriptions for one user
if (subscriptions.length > 1) {
await logger.error(
new Error(
`We may need to refund: User had multiple subscriptions ${user.email} (${customer.id})`
)
);
await emailHelper({
template: 'alert',
message: {
to: config.email.message.from,
subject: `User had multiple subscriptions ${user.email}`
},
locals: {
message: `User ${user.email} (${customer.id}) had multiple subscriptions: ${JSON.stringify(
subscriptions.map((s) => s.id)
)}`
}
});
}
}
งานนี้ตรวจสอบความคลาดเคลื่อนระหว่างฐานข้อมูลของเรากับ Stripe เช่น ที่อยู่อีเมลไม่ตรงกัน หรือมีการสมัครใช้งานหลายรายการ หากพบปัญหาใดๆ ระบบจะบันทึกปัญหาและส่งการแจ้งเตือนไปยังทีมผู้ดูแลระบบของเรา
การซิงโครไนซ์การสมัคร PayPal
เรามีงานที่คล้ายกันสำหรับการสมัครสมาชิก PayPal:
async function syncPayPalSubscriptionPayments() {
const paypalCustomers = await Users.find({
$or: [
{
[config.userFields.paypalSubscriptionID]: { $exists: true, $ne: null }
},
{
[config.userFields.paypalPayerID]: { $exists: true, $ne: null }
}
]
})
// sort by newest customers first
.sort('-created_at')
.lean()
.exec();
await logger.info(
`Syncing payments for ${paypalCustomers.length} paypal customers`
);
// Process each customer and sync their payments
const errorEmails = await pReduce(
paypalCustomers,
// Implementation details...
);
}
งานอัตโนมัติเหล่านี้ทำหน้าที่เป็นตาข่ายความปลอดภัยขั้นสุดท้ายของเรา โดยรับรองว่าฐานข้อมูลของเราสะท้อนสถานะที่แท้จริงของการสมัครสมาชิกและการชำระเงินในทั้ง Stripe และ PayPal เสมอ
การจัดการกรณีขอบ
ระบบการชำระเงินที่แข็งแกร่งต้องสามารถรับมือกับสถานการณ์เฉพาะหน้าต่างๆ ได้อย่างมีประสิทธิภาพ มาดูกันว่าเราจะรับมือกับสถานการณ์ทั่วไปเหล่านี้อย่างไร
การตรวจจับและป้องกันการฉ้อโกง
เราได้นำกลไกการตรวจจับการฉ้อโกงที่ซับซ้อนมาใช้งาน ซึ่งจะระบุและจัดการกิจกรรมการชำระเงินที่น่าสงสัยโดยอัตโนมัติ:
case 'charge.failed': {
// Get all failed charges in the last 30 days
const charges = await stripe.charges.list({
customer: event.data.object.customer,
created: {
gte: dayjs().subtract(1, 'month').unix()
}
});
// Filter for declined charges
const filtered = charges.data.filter(
(d) => d.status === 'failed' && d.failure_code === 'card_declined'
);
// if not more than 5 then return early
if (filtered.length < 5) break;
// Check if user has verified domains
const count = await Domains.countDocuments({
members: {
$elemMatch: {
user: user._id,
group: 'admin'
}
},
plan: { $in: ['enhanced_protection', 'team'] },
has_txt_record: true
});
if (!user.is_banned) {
// If no verified domains, ban the user and refund all charges
if (count === 0) {
// Ban the user
user.is_banned = true;
await user.save();
// Refund all successful charges
}
}
}
โค้ดนี้จะแบนผู้ใช้ที่มีการเรียกเก็บเงินล้มเหลวหลายครั้งและไม่มีโดเมนที่ได้รับการยืนยันโดยอัตโนมัติ ซึ่งเป็นตัวบ่งชี้ที่ชัดเจนว่ามีการดำเนินกิจกรรมฉ้อโกง
การจัดการข้อพิพาท
เมื่อผู้ใช้โต้แย้งค่าใช้จ่าย เราจะยอมรับการเรียกร้องโดยอัตโนมัติและดำเนินการที่เหมาะสม:
case 'CUSTOMER.DISPUTE.CREATED': {
// accept claim
const agent = await paypalAgent();
await agent
.post(`/v1/customer/disputes/${body.resource.dispute_id}/accept-claim`)
.send({
note: 'Full refund to the customer.'
});
// Find the payment in our database
const payment = await Payments.findOne({ $or });
if (!payment) throw new Error('Payment does not exist');
const user = await Users.findById(payment.user);
if (!user) throw new Error('User did not exist for customer');
// Cancel the user's subscription if they have one
if (isSANB(user[config.userFields.paypalSubscriptionID])) {
try {
const agent = await paypalAgent();
await agent.post(
`/v1/billing/subscriptions/${
user[config.userFields.paypalSubscriptionID]
}/cancel`
);
} catch (err) {
// Handle subscription cancellation errors
}
}
}
แนวทางนี้ช่วยลดผลกระทบของข้อพิพาทต่อธุรกิจของเรา พร้อมทั้งยังรับประกันประสบการณ์ที่ดีให้กับลูกค้าอีกด้วย
การนำรหัสกลับมาใช้ใหม่: หลักการ KISS และ DRY
ตลอดระบบการชำระเงินของเรา เรายึดมั่นในหลักการ KISS (Keep It Simple, Stupid) และ DRY (Don't Repeat Yourself) นี่คือตัวอย่างบางส่วน:
-
ฟังก์ชันตัวช่วยที่ใช้ร่วมกัน: เราได้สร้างฟังก์ชันตัวช่วยที่สามารถนำกลับมาใช้ใหม่ได้สำหรับงานทั่วไป เช่น การซิงค์การชำระเงินและการส่งอีเมล
-
การจัดการข้อผิดพลาดที่สอดคล้องกัน: ตัวจัดการเว็บฮุกของ Stripe และ PayPal ต่างก็ใช้รูปแบบเดียวกันสำหรับการจัดการข้อผิดพลาดและการแจ้งเตือนผู้ดูแลระบบ
-
โครงร่างฐานข้อมูลรวม: โครงร่างฐานข้อมูลของเราได้รับการออกแบบมาเพื่อรองรับข้อมูล Stripe และ PayPal โดยมีฟิลด์ทั่วไปสำหรับสถานะการชำระเงิน จำนวนเงิน และข้อมูลแผน
-
การกำหนดค่าแบบรวมศูนย์: การกำหนดค่าที่เกี่ยวข้องกับการชำระเงินจะรวมศูนย์อยู่ในไฟล์เดียว ทำให้อัปเดตราคาและข้อมูลผลิตภัณฑ์ได้ง่าย
ข้อกำหนดการสมัคร VISA การดำเนินการ
นอกจากแนวทาง trifecta ของเราแล้ว เรายังได้นำฟีเจอร์เฉพาะต่างๆ มาใช้เพื่อให้สอดคล้องกับข้อกำหนดการสมัครสมาชิกของ VISA พร้อมกับยกระดับประสบการณ์การใช้งานของผู้ใช้ ข้อกำหนดสำคัญประการหนึ่งของ VISA คือผู้ใช้จะต้องได้รับการแจ้งเตือนก่อนที่จะถูกเรียกเก็บเงินสำหรับการสมัครสมาชิก โดยเฉพาะอย่างยิ่งเมื่อเปลี่ยนจากการสมัครสมาชิกแบบทดลองใช้เป็นการสมัครสมาชิกแบบชำระเงิน
การแจ้งเตือนอีเมลล่วงหน้าการต่ออายุอัตโนมัติ
เราได้สร้างระบบอัตโนมัติที่จะช่วยระบุตัวตนผู้ใช้ที่สมัครทดลองใช้บริการอยู่ และส่งอีเมลแจ้งเตือนก่อนที่จะมีการเรียกเก็บเงินครั้งแรก วิธีนี้ไม่เพียงแต่ช่วยให้เราปฏิบัติตามข้อกำหนดของ VISA เท่านั้น แต่ยังช่วยลดการขอคืนเงินและเพิ่มความพึงพอใจของลูกค้าอีกด้วย
นี่คือวิธีที่เรานำฟีเจอร์นี้มาใช้:
// Find users with trial subscriptions who haven't received a notification yet
const users = await Users.find({
$or: [
{
$and: [
{ [config.userFields.stripeSubscriptionID]: { $exists: true } },
{ [config.userFields.stripeTrialSentAt]: { $exists: false } },
// Exclude subscriptions that have already had payments
...(paidStripeSubscriptionIds.length > 0
? [
{
[config.userFields.stripeSubscriptionID]: {
$nin: paidStripeSubscriptionIds
}
}
]
: [])
]
},
{
$and: [
{ [config.userFields.paypalSubscriptionID]: { $exists: true } },
{ [config.userFields.paypalTrialSentAt]: { $exists: false } },
// Exclude subscriptions that have already had payments
...(paidPayPalSubscriptionIds.length > 0
? [
{
[config.userFields.paypalSubscriptionID]: {
$nin: paidPayPalSubscriptionIds
}
}
]
: [])
]
}
]
});
// Process each user and send notification
for (const user of users) {
// Get subscription details from payment processor
const subscription = await getSubscriptionDetails(user);
// Calculate subscription duration and frequency
const duration = getDurationFromPlanId(subscription.plan_id);
const frequency = getHumanReadableFrequency(duration, user.locale);
const amount = getPlanAmount(user.plan, duration);
// Get user's domains for personalized email
const domains = await Domains.find({
'members.user': user._id
}).sort('name').lean().exec();
// Send VISA-compliant notification email
await emailHelper({
template: 'visa-trial-subscription-requirement',
message: {
to: user.receipt_email || user.email,
...(user.receipt_email ? { cc: user.email } : {})
},
locals: {
user,
firstChargeDate: new Date(subscription.start_time),
frequency,
formattedAmount: numeral(amount).format('$0,0,0.00'),
domains
}
});
// Record that notification was sent
await Users.findByIdAndUpdate(user._id, {
$set: {
[config.userFields.paypalTrialSentAt]: new Date()
}
});
}
การใช้งานนี้ช่วยให้ผู้ใช้ได้รับแจ้งเกี่ยวกับค่าธรรมเนียมที่จะเกิดขึ้นอยู่เสมอ โดยมีรายละเอียดที่ชัดเจนเกี่ยวกับ:
- การเรียกเก็บเงินครั้งแรกจะเกิดขึ้นเมื่อใด
- ความถี่ของการเรียกเก็บเงินในอนาคต (รายเดือน รายปี ฯลฯ)
- จำนวนเงินที่แน่นอนที่จะถูกเรียกเก็บ
- โดเมนใดบ้างที่ครอบคลุมภายใต้การสมัครใช้งาน
ด้วยการทำให้กระบวนการนี้เป็นแบบอัตโนมัติ เราจึงรักษาความสอดคล้องกับข้อกำหนดของ VISA ได้อย่างสมบูรณ์แบบ (ซึ่งกำหนดให้แจ้งล่วงหน้าอย่างน้อย 7 วันก่อนเรียกเก็บเงิน) ในขณะเดียวกันก็ลดการสอบถามการสนับสนุนและปรับปรุงประสบการณ์ผู้ใช้โดยรวม
การจัดการกรณีขอบ
การใช้งานของเรายังรวมถึงการจัดการข้อผิดพลาดที่มีประสิทธิภาพ หากมีข้อผิดพลาดใดๆ ในระหว่างกระบวนการแจ้งเตือน ระบบของเราจะแจ้งเตือนทีมของเราโดยอัตโนมัติ:
try {
await mapper(user);
} catch (err) {
logger.error(err);
// Send alert to administrators
await emailHelper({
template: 'alert',
message: {
to: config.email.message.from,
subject: 'VISA Trial Subscription Requirement Error'
},
locals: {
message: `<pre><code>${safeStringify(
parseErr(err),
null,
2
)}</code></pre>`
}
});
}
ซึ่งช่วยให้มั่นใจได้ว่าแม้ว่าจะมีปัญหาเกิดขึ้นกับระบบการแจ้งเตือน ทีมงานของเราก็สามารถแก้ไขปัญหาได้อย่างรวดเร็วและรักษาการปฏิบัติตามข้อกำหนดของ VISA
ระบบแจ้งเตือนการสมัคร VISA ถือเป็นอีกตัวอย่างหนึ่งของการที่เราสร้างโครงสร้างพื้นฐานการชำระเงินโดยคำนึงถึงทั้งการปฏิบัติตามข้อกำหนดและประสบการณ์ของผู้ใช้ ซึ่งเป็นการเสริมแนวทาง trifecta ของเราเพื่อให้แน่ใจว่าการประมวลผลการชำระเงินมีความน่าเชื่อถือและโปร่งใส
ระยะเวลาทดลองใช้ ### และข้อกำหนดการสมัครใช้งาน
สำหรับผู้ใช้ที่เปิดใช้งานการต่ออายุอัตโนมัติในแผนที่มีอยู่ เราจะคำนวณระยะเวลาทดลองใช้งานที่เหมาะสมเพื่อให้แน่ใจว่าพวกเขาจะไม่ถูกเรียกเก็บเงินจนกว่าแผนปัจจุบันของพวกเขาจะหมดอายุ:
if (
isEnableAutoRenew &&
dayjs(ctx.state.user[config.userFields.planExpiresAt]).isAfter(
dayjs()
)
) {
const hours = dayjs(
ctx.state.user[config.userFields.planExpiresAt]
).diff(dayjs(), 'hours');
// Handle trial period calculation
}
นอกจากนี้ เรายังให้ข้อมูลที่ชัดเจนเกี่ยวกับเงื่อนไขการสมัครสมาชิก รวมถึงความถี่ในการเรียกเก็บเงินและนโยบายการยกเลิก และรวมข้อมูลเมตาโดยละเอียดไว้กับการสมัครสมาชิกแต่ละครั้งเพื่อให้แน่ใจว่ามีการติดตามและการจัดการอย่างถูกต้อง
บทสรุป: ประโยชน์ของแนวทาง Trifecta ของเรา
แนวทางการประมวลผลการชำระเงินแบบ Trifecta ของเรามอบประโยชน์สำคัญหลายประการ:
-
ความน่าเชื่อถือ: โดยการนำการตรวจสอบการชำระเงิน 3 ชั้นมาใช้ เราจึงมั่นใจได้ว่าจะไม่มีการชำระเงินใดที่ขาดหายไปหรือดำเนินการไม่ถูกต้อง
-
ความแม่นยำ: ฐานข้อมูลของเราสะท้อนสถานะที่แท้จริงของการสมัครสมาชิกและการชำระเงินในทั้ง Stripe และ PayPal เสมอ
-
ความยืดหยุ่น: ผู้ใช้สามารถเลือกวิธีการชำระเงินที่ต้องการได้โดยไม่กระทบต่อความน่าเชื่อถือของระบบของเรา
-
ความแข็งแกร่ง: ระบบของเราจัดการกับกรณีขอบต่างๆ ได้อย่างราบรื่น ไม่ว่าจะเป็นความล้มเหลวของเครือข่ายหรือกิจกรรมฉ้อโกง
หากคุณกำลังใช้งานระบบการชำระเงินที่รองรับโปรเซสเซอร์หลายตัว เราขอแนะนำแนวทาง trifecta นี้เป็นอย่างยิ่ง แม้จะต้องใช้ความพยายามในการพัฒนาเบื้องต้นมากกว่า แต่ประโยชน์ในระยะยาวในแง่ของความน่าเชื่อถือและความแม่นยำนั้นคุ้มค่าอย่างยิ่ง
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการส่งต่ออีเมลและบริการอีเมลที่เน้นความเป็นส่วนตัวของเรา โปรดไปที่ เว็บไซต์