أتمتة الفوترة المتكررة: تجنب الفوضى الزمنية في اختبارات الاشتراكات
مقدمة في أتمتة “Automation” الأحداث المتكررة
إدارة الأحداث المتكررة مثل دفع الاشتراكات هي عنصر أساسي في تطبيقات البرمجيات الحديثة، خاصةً تلك التي تعتمد على نموذج الاشتراكات. أتمتة هذه العملية تضمن دفع الفواتير في الوقت المحدد وتجربة مستخدم متسقة، مع تقليل مخاطر الأخطاء البشرية. ومع ذلك، فإن أتمتة الأحداث المتكررة تأتي مع تحدياتها، مثل التعامل مع الفروقات في دورات الفوترة، سنوات الكبيسة، الفروقات الزمنية، وحالات فشل الدفع.
سنستعرض في هذا المقال كيفية تصميم وتنفيذ نظام إدارة اشتراكات فعال باستخدام لغة Python، مع تغطية حالات الحافة واختبار سير عمل الدفع.
تصميم خدمة الاشتراك
لبناء نظام إدارة اشتراكات قوي، يجب تحديد بنية واضحة. جوهر هذا النظام هو فئة Subscription التي تحتوي على جميع التفاصيل ذات الصلة بالاشتراك مثل معرف المستخدم، نوع الخطة، تاريخ البداية، تاريخ الفوترة التالي، الحالة، المبلغ، ومعلومات الإلغاء (إن وجدت). يساعد استخدام Python’s @dataclass
في إنشاء هذه الفئة بسهولة مع تقليل الأكواد الزائدة.
@dataclass
class Subscription:
user_id: str
plan_type: str
start_date: datetime
next_billing_date: datetime
status: str
amount: float
cancelled_at: Optional[datetime] = None
حساب تاريخ الفوترة التالي
واحدة من المهام الرئيسية لنظام إدارة الاشتراكات هي حساب تاريخ الفوترة التالي بناءً على نوع الخطة. يجب أن يأخذ النظام بعين الاعتبار اختلاف عدد الأيام في الأشهر وسنوات الكبيسة.
def _calculate_next_billing_date(self, current_date: datetime, plan_type: str) -> datetime:
if plan_type == "monthly":
if current_date.month == 12:
next_month = 1
next_year = current_date.year + 1
else:
next_month = current_date.month + 1
next_year = current_date.year
_, last_day = calendar.monthrange(next_year, next_month)
return datetime(
year=next_year,
month=next_month,
day=min(current_date.day, last_day),
hour=current_date.hour,
minute=current_date.minute
)
elif plan_type == "yearly":
next_year = current_date.year + 1
if current_date.month == 2 and current_date.day == 29:
if not calendar.isleap(next_year):
return datetime(next_year, 2, 28, current_date.hour, current_date.minute)
return datetime(next_year, current_date.month, current_date.day, current_date.hour, current_date.minute)
raise ValueError(f"Unsupported plan type: {plan_type}")
معالجة حالات الحافة
- نهاية الشهر: يجب أن ينتقل المستخدم المشترك في 31 يناير إلى 28 أو 29 فبراير بشكل صحيح.
- سنوات الكبيسة: الاشتراكات التي تبدأ في 29 فبراير يجب أن تأخذ في الحسبان السنوات غير الكبيسة.
- المناطق الزمنية: يجب أن تتماشى تواريخ الفوترة مع المناطق الزمنية المختلفة لتجنب الفروقات بين التواريخ المعروضة والحقيقية.
بناء سير عمل معالجة الدفع
التعامل مع نجاح وفشل الدفع
سير عمل الدفع مسؤول عن التحقق من الاشتراكات النشطة وشحن المستخدمين عند الوصول إلى تاريخ الفوترة التالي.
def process_recurring_payments(self):
current_time = self.time_provider.now()
for subscription in self.subscriptions.values():
if (subscription.status == "active" and
subscription.next_billing_date <= current_time):
try:
self.payment_gateway.charge(subscription.user_id, subscription.amount)
subscription.next_billing_date = self._calculate_next_billing_date(
subscription.next_billing_date,
subscription.plan_type
)
except PaymentError:
subscription.status = "payment_failed"
الإلغاء وتأثيره على الفوترة
عند إلغاء الاشتراك، يجب تحديث حالته إلى “ملغي” وتسجيل وقت الإلغاء.
def cancel_subscription(self, user_id: str):
if user_id in self.subscriptions:
self.subscriptions[user_id].status = "cancelled"
self.subscriptions[user_id].cancelled_at = self.time_provider.now()
اختبار الأتمتة – Automation Testing
لضمان موثوقية النظام، يتم اختبار سيناريوهات العالم الواقعي وحالات الحافة باستخدام أدوات مثل unittest.mock لمحاكاة الاعتماديات الخارجية.
سيناريوهات الاختبار
نجاح الفوترة
def test_successful_recurring_payment(subscription_service, mock_payment_gateway, mock_time_provider):
subscription = subscription_service.create_subscription("user1", "monthly", 9.99)
mock_time_provider.now.return_value = subscription.next_billing_date
subscription_service.process_recurring_payments()
mock_payment_gateway.charge.assert_called_once_with("user1", 9.99)
assert subscription.next_billing_date == datetime(2024, 3, 1, 12, 0)
فشل الدفع
def test_failed_recurring_payment(subscription_service, mock_payment_gateway, mock_time_provider):
subscription = subscription_service.create_subscription("user1", "monthly", 9.99)
mock_payment_gateway.charge.side_effect = PaymentError()
mock_time_provider.now.return_value = subscription.next_billing_date
subscription_service.process_recurring_payments()
assert subscription.status == "payment_failed"
حالات الحافة
def test_edge_cases_monthly_billing(subscription_service, mock_time_provider):
mock_time_provider.now.return_value = datetime(2024, 1, 31, 12, 0)
jan_subscription = subscription_service.create_subscription("user1", "monthly", 9.99)
assert jan_subscription.next_billing_date == datetime(2024, 2, 29, 12, 0)
الخلاصة
أتمتة الفوترة المتكررة هي وسيلة فعالة لتحسين العمليات وتعزيز تجربة المستخدم. من خلال تنفيذ ممارسات مثل التحقق القوي، الإشعارات الواضحة، والتعامل الفعال مع حالات الفشل، يمكن بناء نظام أتمتة موثوق يقلل الأخطاء ويضمن رضا العملاء.
المصدر: thegreenreport