১৫ ডিসে, ২০২৪·7 মিনিট পড়তে

নিরাপদ বিলিং আপডেটের জন্য idempotent পেমেন্ট ওয়েবহুক চেকলিস্ট

ডুপ্লিকেট ইভেন্ট ডিডুপ করা, রিট্রাই হ্যান্ডল করা, এবং ইনভয়েস, সাবস্ক্রিপশন ও entitlements নিরাপদে আপডেট করার জন্য idempotent পেমেন্ট ওয়েবহুক চেকলিস্ট।

নিরাপদ বিলিং আপডেটের জন্য idempotent পেমেন্ট ওয়েবহুক চেকলিস্ট

কেন পেমেন্ট ওয়েবহুকগুলো ডুপ্লিকেট আপডেট তৈরি করে

পেমেন্ট ওয়েবহুক হলো সেই বার্তা যা আপনার পেমেন্ট প্রোভাইডার আপনার ব্যাকএন্ডে পাঠায় যখন কিছু গুরুত্বপূর্ণ ঘটে—উদাহরণস্বরূপ চার্জ সফল হয়, ইনভয়েস পেইড হয়, সাবস্ক্রিপশন রিনিউ হয়, বা রিফান্ড ইস্যু হয়। বোঝানো হচ্ছে: “এটিই ঘটেছে। আপনার রেকর্ড আপডেট করুন।”

ডুপ্লিকেট ঘটে কারণ ওয়েবহুক ডেলিভারি নির্ভরযোগ্য করার উদ্দেশ্যে করা হয়েছে, "একবারই" না। যদি আপনার সার্ভার ধীর হয়, টাইমআউট করে, একটি ত্রুটি ফেরত দেয়, বা সাময়িকভাবে অনুপলব্ধ থাকে, প্রোভাইডার সাধারণত একই ইভেন্ট পুনরায় পাঠায়। এছাড়া একই বাস্তব-জগত ক্রিয়াকলাপের দুটি ভিন্ন ইভেন্ট দেখতে পারেন (যেমন, একটি ইনভয়েস ইভেন্ট এবং একটি পেমেন্ট ইভেন্ট একই পেমেন্টের সাথে সম্পর্কিত)। ইভেন্টগুলো আউট অব অর্ডারও এসে যেতে পারে, বিশেষত দ্রুত ফলো-আপগুলোর ক্ষেত্রে যেমন রিফান্ড।

যদি আপনার হ্যান্ডলার idempotent না হয়, এটি একই ইভেন্ট দুবার প্রয়োগ করতে পারে, যা কাস্টমার এবং ফাইন্যান্স টিম তড়িৎভাবে লক্ষ্য করে:\n\n- একই ইনভয়েস দুইবার পেইড হিসেবে চিহ্নিত হওয়া, ডুপ্লিকেট অ্যাকাউন্টিং এন্ট্রি তৈরি করা\n- রিনিউয়াল দুইবার প্রয়োগ হওয়া, অ্যাক্সেস বেশি সময়ের জন্য বাড়ানো\n- entitlements দুবার গ্রান্ট হওয়া (অতিরিক্ত ক্রেডিট, সিট বা ফিচার)\n- রিফান্ড বা চার্জব্যাক সঠিকভাবে অ্যাক্সেস উল্টায় না করা

এটি কেবল "সেরা অনুশীলন" নয়। এটা নির্ভরযোগ্য বিলিং এবং সমর্থন টিকেট তৈরি করে না—এর মধ্যে পার্থক্য।

এই চেকলিস্টের উদ্দেশ্য সহজ: প্রতিটি ইনকামিং ইভেন্টকে "সর্বাধিক একবার প্রয়োগ করুন" বলে বিবেচনা করা। আপনি প্রতিটি ইভেন্টের জন্য একটি স্থায়ী আইডি সংরক্ষণ করবেন, রিট্রাই নিরাপদভাবে হ্যান্ডল করবেন, এবং ইনভয়েস, সাবস্ক্রিপশন, এবং entitlements নিয়ন্ত্রিতভাবে আপডেট করবেন। আপনি যদি AppMaster (appmaster.io) মত নো-কোড টুলে ব্যাকএন্ড তৈরি করেন, একই নিয়ম প্রযোজ্য: একটি স্পষ্ট ডেটা মডেল এবং একটি পুনরাবৃত্তযোগ্য হ্যান্ডলার ফ্লো দরকার যা রিট্রাইয়ের অধীনে সঠিক থাকে।

ওয়েবহুকগুলির জন্য প্রয়োগযোগ্য idempotency বেসিক্স

Idempotency মানে একই ইনপুট একাধিকবার প্রক্রিয়াকরণ করলে একই ফাইনাল স্টেট পাওয়া যায়। বিলিং শব্দে: একটি ইনভয়েস একবারই পেইড হিসেবে থাকবে, একটি সাবস্ক্রিপশন একবারই আপডেট হবে, এবং অ্যাক্সেস একবারই গ্রান্ট হবে—যদিও ওয়েবহুক দুবার ডেলিভার করা হয়েছে।

প্রোভাইডাররা তখনই রিট্রাই করে যখন আপনার এন্ডপয়েন্ট টাইমআউট করে, 5xx রিটার্ন করে, বা নেটওয়ার্ক ড্রপ করে। সেই রিট্রাইগুলো একই ইভেন্টটি পুনরায় পাঠায়। এটা আলাদা একটি নতুন ইভেন্ট থেকে, যা বাস্তবে আলাদা পরিবর্তন নির্দেশ করে—যেমন কয়েক দিন পরে হওয়া একটি রিফান্ড। নতুন ইভেন্টগুলোর আইডি আলাদা।

এটা কাজ করাতে, আপনার দুইটি জিনিস দরকার: স্থিতিশীল আইডেন্টিফায়ার এবং যা আপনি ইতিমধ্যে দেখেছেন তার একটি ছোট "মেমরি" বা রেকর্ড।

কোন আইডিগুলো গুরুত্বপূর্ণ (এবং কী সংরক্ষণ করবেন)

অধিকাংশ পেমেন্ট প্ল্যাটফর্মে একটি event ID থাকে যা ওয়েবহুক ইভেন্টের জন্য ইউনিক। কিছু ক্ষেত্রে একটি request ID, idempotency key, বা পে‍মেন্ট অবজেক্ট আইডি (যেমন charge বা payment intent) পে-লোডে থাকতে পারে।

সংরক্ষণ করুন যেটা আপনাকে এক প্রশ্নের উত্তর দিতে সাহায্য করবে: “আমি কি এই এক্সাক্ট ইভেন্টটি আগে থেকেই প্রয়োগ করেছি?”

প্রায়োগিক ন্যূনতমঃ

  • Event ID (unique key)
  • Event type (ডিবাগিং এর জন্য উপকারী)
  • প্রাপ্ত টাইমস্ট্যাম্প
  • প্রসেসিং স্ট্যাটাস (processed/failed)
  • প্রভাবিত কাস্টমার, ইনভয়েস, বা সাবস্ক্রিপশন-এর রেফারেন্স

মূল কৌশল হলো ইভেন্ট আইডি একটি টেবিলে ইউনিক কনস্ট্রেন্টসহ সংরক্ষণ করা। তখন আপনার হ্যান্ডলার নিরাপদে এটা করতে পারে: প্রথমে event ID ঢুকান; যদি এটি ইতিমধ্যেই থাকে, থেমে যান এবং 200 রিটার্ন করুন।

কতদিন ডেডুপ রেকর্ড রাখা উচিত

ডিলেইড রিট্রাই এবং তদন্ত কভার করার জন্য ডেডুপ রেকর্ড পর্যাপ্ত সময় রাখুন। সাধারণ উইন্ডো হলো 30 থেকে 90 দিন। যদি আপনি চার্জব্যাক, বিতর্ক, বা দীর্ঘ সাবস্ক্রিপশন চক্র নিয়ে কাজ করেন, আরও লম্বা রাখুন (6 থেকে 12 মাস), এবং পুরোনো সারি পিউর্জ করুন যাতে টেবিল দ্রুত থাকে।

একটি জেনারেটেড ব্যাকএন্ডে যেমন AppMaster-এ, এটি পরিষ্কারভাবে একটি WebhookEvents মডেলে ম্যাপ করে — যেখানে event ID-র উপর একটি ইউনিক ফিল্ড থাকবে, এবং একটি Business Process থাকবে যা ডুপ্লিকেট সনাক্ত হলে দ্রুত বেরিয়ে যায়।

ইভেন্ট ডেডুপ করার জন্য একটি সহজ ডেটা মডেল ডিজাইন করুন

একটি ভাল ওয়েবহুক হ্যান্ডলার বেশিরভাগই একটি ডেটা সমস্যা। যদি আপনি প্রতিটি প্রোভাইডার ইভেন্ট একবারই রেকর্ড করতে পারেন, তারপর সবকিছু অনেক নিরাপদ হয়ে যায়।

একটি রসিদ-লগের মতো একটি টেবিল দিয়ে শুরু করুন। PostgreSQL-এ (এবং AppMaster-এ Data Designer এ মডেল করলে) এটাকে ছোট ও কড়া রাখুন যাতে ডুপ্লিকেট দ্রুত ব্যর্থ হয়।

ন্যূনতম যা প্রয়োজন

webhook_events টেবিলের জন্য একটি ব্যবহারিক বেসলাইন:

  • provider (text, যেমন "stripe")
  • provider_event_id (text, প্রয়োজনীয়)
  • status (text, যেমন "received", "processed", "failed")
  • processed_at (timestamp, nullable)
  • raw_payload (jsonb বা text)

(provider, provider_event_id)-তে একটি ইউনিক কনস্ট্রেইনট যোগ করুন। এই একটাই নিয়ম আপনার প্রধান ডেডুপ গার্ডরেইল।

আপনি যে ব্যবসায়িক আইডিগুলো ব্যবহার করবেন সেগুলোও রাখতে চান যাতে আপডেট করার জন্য রেকর্ড খুঁজে পান। এগুলো ওয়েবহুক ইভেন্ট আইডি থেকে আলাদা। সাধারণ উদাহরণ: customer_id, invoice_id, এবং subscription_id — এগুলোকে টেক্সট হিসেবে রাখুন কারণ প্রোভাইডাররা প্রায়ই নন-নিউমেরিক আইডি ব্যবহার করে।

Raw payload বনাম parsed fields

ডিবাগ ও পরে রি-প্রসেস করার জন্য raw payload সংরক্ষণ করুন। parsed ফিল্ডগুলো কোয়েরি ও রিপোর্টিংকে সহজ করে, কিন্তু কেবল আপনি যে ফিল্ডগুলো ব্যবহার করেন ততটুকুই সংরক্ষণ করুন।

সরল পদ্ধতি:

  • সবসময় raw_payload সংরক্ষণ করুন
  • যেসব parsed IDs-এ আপনি প্রায়ই কোয়েরি করবেন সেগুলো আলাদাভাবে রাখুন (customer, invoice, subscription)
  • ফিল্টার করার জন্য একটি normalized event_type (text) রাখুন

যদি একটি invoice.paid ইভেন্ট দুবার আসে, আপনার ইউনিক কনস্ট্রেইনট দ্বিতীয় ইনসার্ট ব্লক করবে। আপনার কাছে অডিটের জন্য raw payload থাকবে, এবং parsed invoice ID-টি আপনাকে প্রথমবারে আপডেট করা ইনভয়েস রেকর্ড সহজেই খুঁজে দিতে পারে।

ধাপে ধাপে: একটি নিরাপদ ওয়েবহুক হ্যান্ডলার ফ্লো

একটি নিরাপদ হ্যান্ডলার উদ্দেশ্যগতভাবে বোরিং। এটা প্রতিবার একইভাবে আচরণ করে, এমনকি প্রোভাইডার যদি একই ইভেন্ট রিট্রাই করে বা ইভেন্টগুলো আউট অব অর্ডার ডেলিভার করে।

প্রতি বার অনুসরণ করার ৫-ধাপ ফ্লো

  1. সিগনেচার যাচাই করুন এবং payload পার্স করুন। সিগনেচার চেক ব্যর্থ হলে, অপ্রত্যাশিত ইভেন্ট টাইপ হলে, বা পার্স না হলে অনুরোধকে নাকচ করুন।

  2. বিলিং ডেটা স্পর্শ করার আগে ইভেন্ট রেকর্ড লিখুন। provider event ID, টাইপ, তৈরি হওয়ার সময়, এবং raw payload (বা একটি হ্যাশ) সংরক্ষণ করুন। যদি event ID ইতিমধ্যে থাকে, এটাকে ডুপ্লিকেট হিসেবে বিবেচনা করুন এবং রোজগার বন্ধ করুন।

  3. ইভেন্টকে একটি একক “owner” রেকর্ডে ম্যাপ করুন। সিদ্ধান্ত নিন আপনি কী আপডেট করবেন: invoice, subscription, বা customer। আপনার রেকর্ডগুলোতে এক্সটার্নাল আইডি সংরক্ষণ করুন যাতে সরাসরি সেগুলো lookup করা যায়।

  4. একটি নিরাপদ স্টেট চেঞ্জ প্রয়োগ করুন। শুধুমাত্র স্টেটকে সামনে নেওয়া হবে। একটি পরে আসা “invoice.updated” ফলে পেইড ইনভয়েসকে উপসর্গ না করে উলটে দেবেন না। আপনি যা প্রয়োগ করেছেন তা রেকর্ড করুন (পুরনো স্টেট, নতুন স্টেট, টাইমস্ট্যাম্প, event ID) যাতে অডিট মেলে।

  5. দ্রুত রেসপন্স দিন এবং আউটকাম লগ করুন। ইভেন্টটি নিরাপদে সংরক্ষিত এবং প্রক্রিয়াজাত বা উপেক্ষিত হওয়ার পরে সাফল্য রিটার্ন করুন। লগ করুন সেটা প্রসেসড, ডেডুপড, বা রিজেক্টেড কেন ছিল।

AppMaster-এ এটি সাধারণত একটি ওয়েবহুক ইভেন্টস টেবিল এবং একটি Business Process হয়ে ওঠে যা "seen event ID?" চেক করে এবং তারপর নূন্যতম আপডেট ধাপগুলো চালায়।

রিট্রাই, টাইমআউট, এবং আউট অব অর্ডার ডেলিভারি হ্যান্ডল করা

আত্মবিশ্বাসের সঙ্গে ডিপ্লয় করুন
আপনার জেনারেটেড ব্যাকএন্ড আপনার ক্লাউডে ডিপ্লয় করুন এবং স্কেলে ওয়েবহুক প্রসেসিং পূর্বানুমানযোগ্য রাখুন।
ডেপ্লয় করুন

প্রোভাইডাররা তখনই ওয়েবহুক রিট্রাই করে যখন তারা দ্রুত সফল রেসপন্স পায় না। তারা ইভেন্টগুলো আউট অব অর্ডারও পাঠাতে পারে। আপনার হ্যান্ডলারকে নিরাপদ থাকতে হবে যখন একই আপডেট দুবার আসে, অথবা পরে যাওয়া আপডেট আগে আসে।

একটি বাস্তবিক নিয়ম: দ্রুত রেসপন্স দিন, কাজ পরে করুন। ওয়েবহুক অনুরোধকে একটি রসিদ হিসেবে বিবেচনা করুন, ভারী লজিক চালানোর জায়গা নয়। যদি আপনি অনুরোধে তৃতীয়-পক্ষ API কল করেন, PDF জেনারেট করেন, বা আকাউন্টগুলো রিক্যালকুলেট করেন, তাহলে টাইমআউট বাড়ে এবং আরও রিট্রাই ট্রিগার হয়।

আউট অব অর্ডার: নতুনতর ট্রুথ ধরে রাখুন

আউট অব অর্ডার ডেলিভারি স্বাভাবিক। কোনো পরিবর্তন প্রয়োগ করার আগে দুইটি চেক ব্যবহার করুন:

  • টাইমস্ট্যাম্প তুলনা: একটি ইভেন্ট কেবল তখনই প্রয়োগ করুন যদি এটি ওই অবজেক্টের জন্য আপনার আগে যে স্টোর করা আছে তার চেয়ে নতুন হয় (invoice, subscription, entitlement)।
  • টাইমস্ট্যাম্প অনির্দিষ্ট হলে বা কাছাকাছি হলে স্ট্যাটাস প্রায়োরিটি ব্যবহার করুন: paid > open, canceled > active, refunded > paid ইত্যাদি।

যদি আপনি ইতিমধ্যেই একটি ইনভয়েস পেইড হিসেবে রেকর্ড করে রাখেন এবং পরে একটা পুরনো "open" ইভেন্ট আসে, সেটাকে উপেক্ষা করুন। যদি আপনি "canceled" রেকর্ড করেন এবং পরে পুরনো "active" আপডেট আসে, canceled রাখুন।

Ignore বনাম queue

যখন আপনি প্রমাণ করতে পারেন ইভেন্টটা স্টেইল বা আগে থেকেই প্রয়োগ করা হয়েছে (একই event ID, পুরোনো টাইমস্ট্যাম্প, কম স্ট্যাটাস প্রায়োরিটি), তখন ইভেন্ট উপেক্ষা করুন। যখন ইভেন্টটি কোন ডেটার উপর নির্ভর করে যা এখনও নেই (যেমন সাবস্ক্রিপশন আপডেট আগে আসে যখন কাস্টমার রেকর্ড নেই), তখন ইভেন্টটি কিউ করুন।

একটি ব্যবহারিক প্যাটার্ন:

  • ইভেন্টটি তৎক্ষণাৎ প্রসেসিং স্টেটসহ সঞ্চয় করুন (received, processing, done, failed)
  • যদি ডিপেনডেন্সি মিসিং থাকে তবে সেটাকে waiting হিসেবে চিহ্নিত করুন এবং ব্যাকগ্রাউন্ডে রিট্রাই করুন
  • একটি রিট্রাই লিমিট সেট করুন এবং পুনরাবৃত্ত ব্যর্থতার পর এলার্ট জেনারেট করুন

AppMaster-এ, এইটা ওয়েবহুক ইভেন্টস টেবিল এবং একটি Business Process-এ ভালভাবে ফিট করে যা রিকোয়েস্ট দ্রুত acknowledge করে এবং কিউড ইভেন্টগুলো অ্যাসিঙ্ক্রোনাসভাবে প্রসেস করে।

ইনভয়েস, সাবস্ক্রিপশন, এবং entitlements নিরাপদভাবে আপডেট করা

ডেডুপ্লিকেশন হ্যান্ডল করার পর পরবর্তী ঝুঁকি হলো বিভক্ত বিলিং স্টেট: ইনভয়েস পেইড দেখাচ্ছে, কিন্তু সাবস্ক্রিপশন এখনও পাস্ট ডিউ, অথবা অ্যাক্সেস দুইবার গ্রান্ট হয়ে কখনো উল্টায়া যায়নি। প্রতিটি ওয়েবহুককে একটি স্টেট ট্রান্সিশন হিসাবে বিবেচনা করুন এবং এটিকে এক অ্যাটমিক আপডেটে প্রয়োগ করুন।

ইনভয়েস: স্ট্যাটাস চেঞ্জগুলো মনোটোনিক রাখুন

ইনভয়েসের স্টেটগুলো paid, voided, refunded ইত্যাদি হতে পারে। আংশিক পেমেন্টও দেখা যেতে পারে। কোন ইভেন্ট সবশেষ এসেছে সেটাই না দেখে ইনভয়েসকে টগ করবেন না। বর্তমান স্ট্যাটাস এবং মূল টোটালগুলো (amount_paid, amount_refunded) সংরক্ষণ করুন এবং কেবল সামনে-নিরাপদ ট্রানজিশন অনুমোদন করুন।

প্রায়োগিক নিয়মগুলো:

  • ইনভয়েসকে শুধু প্রথমবারই paid হিসেবে চিহ্নিত করুন যখন paid ইভেন্ট প্রথমবার আসে।
  • রিফান্ডের জন্য amount_refunded বাড়ান ইনভয়েস টোটালের আপ টু; কখনো এটি কমান না।
  • ইনভয়েস voided হলে fulfillment অ্যাকশন বন্ধ করুন, কিন্তু অডিটের জন্য রেকর্ড রাখুন।
  • আংশিক পেমেন্টের ক্ষেত্রে, পরিমাণ আপডেট করুন কিন্তু "fully paid" সুবিধা দেবেন না যতক্ষণ না পুরো টোটাল কভার হয়।

সাবস্ক্রিপশন ও entitlements: একবার গ্রান্ট, একবার রিভোক

সাবস্ক্রিপশনে রিনিউয়াল, ক্যানসেলেশন, এবং গ্রেস পিরিয়ড থাকে। সাবস্ক্রিপশন স্ট্যাটাস ও পিরিয়ড বাউন্ডারিগুলো (current_period_start/end) রাখুন, তারপর ঐ ডেটা থেকে entitlement উইন্ডো নির্ধারণ করুন। Entitlements গুলো একটি একক boolean-এর মতো না রেখে স্পষ্ট রেকর্ড হিসেবে রাখুন।

অ্যাক্সেস কন্ট্রোলের জন্য:

  • প্রতিটি ইউজার-প্রোডাক্ট-পিরিয়ড (বা প্রতিটি সাবস্ক্রিপশন) এর জন্য এক entitlement গ্রান্ট
  • অ্যাক্সেস শেষ হলে একটি রিভোকেশন রেকর্ড (ক্যানসেলেশন, রিফান্ড, চার্জব্যাক) তৈরি
  • কোন ওয়েবহুক ইভেন্টটি প্রতিটি পরিবর্তন করেছিল তা রেকর্ড করে একটি অডিট ট্রেইল

বিভক্ত স্টেট এড়াতে একটি ট্রানজেকশন ব্যবহার করুন

ইনভয়েস, সাবস্ক্রিপশন, এবং entitlement আপডেটগুলো একক ডাটাবেস ট্রানজেকশনে প্রয়োগ করুন। বর্তমান সারি পড়ুন, চেক করুন কি এই ইভেন্টটি ইতিমধ্যেই প্রয়োগ হয়েছে, তারপর সব পরিবর্তন একসাথে লিখুন। যদি কিছু ব্যর্থ হয়, roll back করুন যাতে আপনি “paid invoice কিন্তু no access” বা উল্টোটাও না পান।

AppMaster-এ এটি প্রায়ই একটি একক Business Process ফ্লো হিসেবে ম্যাপ হয় যা PostgreSQL-এ একটি নিয়ন্ত্রিত পথ দিয়ে আপডেট করে এবং বিজনেস চেঞ্জের পাশাপাশি একটি অডিট এন্ট্রি লেখে।

ওয়েবহুক এন্ডপয়েন্টের জন্য সিকিউরিটি ও ডেটা সেফটি চেক

আপনার বিলিং ডেটা মডেল ডিজাইন করুন
AppMaster Data Designer ব্যবহার করে WebhookEvents, invoices, এবং entitlements PostgreSQL-এ মডেল করুন।
বিল্ড শুরু করুন

ওয়েবহুক সিকিউরিটি সঠিকতার অংশ। যদি একজন অ্যাটাকার আপনার এন্ডপয়েন্টে পোস্ট করতে পারে, তারা নকল “paid” স্টেট তৈরি করার চেষ্টা করতে পারে। এমনকি ডেডুপ্লিকেশন থাকলেও, ইভেন্টটি আসল নাকি না সেটা প্রমাণ করা আপনার দায়িত্ব ও গ্রাহক ডেটা সুরক্ষিত রাখা জরুরি।

বিলিং ডেটা স্পর্শ করার আগে প্রেরক যাচাই করুন

প্রতিটি রিকোয়েস্টে সিগনেচার ভ্যালিডেট করুন। Stripe-এ এটি সাধারণত Stripe-Signature হেডার চেক করা, র-রিকোয়েস্ট বডি (কনভার্ট করা JSON নয়) ব্যবহার করে, এবং পুরনো টাইমস্ট্যাম্প রেজেক্ট করে করা হয়। অনুপস্থিত হেডারগুলোকে কঠোরভাবে ব্যর্থ হিসেবে নিন।

প্রাথমিকভাবে যাচাই করুন: সঠিক HTTP মেথড, Content-Type, এবং প্রয়োজনীয় ফিল্ডগুলো (event id, type, এবং যে অবজেক্ট আইডি দিয়ে আপনি ইনভয়েস বা সাবস্ক্রিপশন খুঁজবেন)। AppMaster-এ এটি করলে সাইনিং সিক্রেটগুলো environment variables বা secure config-এ রাখুন, ডাটাবেস বা ক্লায়েন্ট কোডে নয়।

দ্রুত সিকিউরিটি চেকলিস্ট:

  • সঠিক সিগনেচার এবং ফ্রেশ টাইমস্ট্যাম্প না থাকলে রিকোয়েস্ট নাকচ করুন
  • প্রত্যাশিত হেডার ও কন্টেন্ট টাইপ দাবি করুন
  • ওয়েবহুক হ্যান্ডলারের জন্য least-privilege ডাটাবেস অ্যাকসেস ব্যবহার করুন
  • সিক্রেটগুলো টেবিলে রাখবেন না—env/config ব্যাবহার করুন; প্রয়োজনে রোটেট করুন
  • ইভেন্ট নিরাপদে সংরক্ষিত হওয়ার পরে কেবল 2xx রিটার্ন করুন

সিক্রেট ফাঁস না করে লোগস উপযোগী রাখুন

রিট্রাই ও বিতর্ক ডিবাগ করার জন্য পর্যাপ্ত লগ রাখুন, কিন্তু সংবেদনশীল মান ফাঁস করবেন না। PII-এর একটি নিরাপদ সাবসেট সংরক্ষণ করুন: প্রোভাইডার কাস্টমার ID, ইন্টারনাল ইউজার ID, এবং সম্ভাব্যভাবে মাস্ক করা ইমেইল (যেমন a***@domain.com)। কখনো পূর্ণ কার্ড ডেটা, পূর্ণ ঠিকানা, বা কাঁচা অথরাইজেশন হেডার সংরক্ষণ করবেন না।

লগগুলো যা পুনর্নির্মাণে সাহায্য করবে তা রাখুন:

  • প্রোভাইডার ইভেন্ট আইডি, টাইপ, তৈরি সময়
  • ভেরিফিকেশন ফলাফল (signature ok/failed) কিন্তু সিগনেচার সংরক্ষণ করবেন না
  • ডেডুপ সিদ্ধান্ত (নতুন বনাম আগেই প্রসেসড)
  • ইন্টারনাল রেকর্ড আইডি(গুলি) যা টাচ করা হয়েছে (invoice/subscription/entitlement)
  • ত্রুটি কারণ এবং রিট্রাই কাউন্ট (যদি আপনি রিট্রাই কিউ করেন)

বেসিক অ্যাবিউজ প্রোটেকশন যোগ করুন: IP দিয়ে রেট লিমিট করুন এবং (যদি সম্ভব) কেবল পরিচিত প্রোভাইডার IP রেঞ্জ থেকে অতিরিক্ত লকডাউন বিবেচনা করুন।

ডাবল চার্জ বা ডাবল অ্যাক্সেস ঘটায় এমন সাধারণ ভুলগুলো

ওয়েবহুক লজিককে পুনরাবৃত্তিমূলক করুন
৫-ধাপের webhook ফ্লোকে একটি ভিজ্যুয়াল Business Process এ রূপান্তর করুন যা আপনি টেস্ট ও পুনরায় চালাতে পারেন।
শুরু করুন

অধিকাংশ বিলিং বাগ গণিতের সমস্যা নয়। এগুলো ঘটে যখন আপনি একটি ওয়েবহুক ডেলিভারিকে এক নির্ভরযোগ্য বার্তার মতো ধরেন।

সবচেয়ে প্রচলিত ভুলগুলো যা ডুপ্লিকেট আপডেট ঘটায়:

  • টাইমস্ট্যাম্প বা অ্যামাউন্ট দিয়ে ডেডুপ করা বরং না করে event ID দিয়ে করা। ভিন্ন ইভেন্ট একই অ্যামাউন্ট শেয়ার করতে পারে, এবং রিট্রাই মিনিট পরে আসতে পারে। প্রোভাইডারের ইউনিক event ID ব্যবহার করুন।
  • সিগনেচার যাচাই করার আগে ডাটাবেস আপডেট করা। প্রথমে যাচাই করুন, তারপর পার্স করুন, তারপর অ্যাক্ট করুন।
  • প্রতিটি ইভেন্টকে ট্রুথ সোর্স হিসেবে আচরণ করা বাগ করে—বর্তমান স্টেট চেক না করা। একটি ইনভয়েস আগে থেকেই paid, refunded বা void হলে অন্ধভাবে paid চিহ্ন করবেন না।
  • একই ক্রয়ের জন্য একাধিক entitlement তৈরি করা। রিট্রাইগুলো ডুপ্লিকেট রো তৈরি করতে পারে। "ensure entitlement exists for subscription_id" ধরনের upsert ভাল।
  • নোটিফিকেশন সার্ভিস ডাউন হলে পুরো হ্যান্ডলার ফেল করা। ইমেইল, SMS, Slack ইত্যাদি বিলিং ব্লক করা উচিত নয়। নোটিফিকেশনগুলো কিউ করুন এবং কোর বিলিং চেঞ্জগুলো নিরাপদে সঞ্চয় করার পর সাফল্য রিটার্ন করুন।

একটি সাধারণ উদাহরণ: একটি রিনিউয়াল ইভেন্ট দুবার আসে। প্রথম ডেলিভারি entitlement রো তৈরি করে। রিট্রাই দ্বিতীয় রো তৈরি করে, আপনার অ্যাপ "দুইটি অ্যাকটিভ entitlement" দেখে অতিরিক্ত সিট বা ক্রেডিট দেয়।

AppMaster-এ, সমাধানটা বেশিরভাগই ফ্লো নিয়ে: প্রথমে যাচাই, ইউনিক কনস্ট্রেইন্টসহ ইভেন্ট রেকর্ড ইনসার্ট, স্টেট চেক সহ বিলিং আপডেট, এবং সাইড-এফেক্টগুলো (ইমেইল, রিসিট) অ্যাসিঙ্ক স্টেপে পাঠানো যাতে সেগুলো রিট্রাই স্টর্ম ট্রিগার না করে।

বাস্তবসম্মত উদাহরণ: ডুপ্লিকেট রিনিউয়াল + পরে রিফান্ড

এই প্যাটার্নটি ভয়াবহ মনে হতে পারে, কিন্তু আপনার হ্যান্ডলার যদি সেফলি পুনরায় চালানো যায় এমন হয় তবে এটি ম্যানেজেবল।

একজন গ্রাহক মাসিক প্ল্যানে আছে। Stripe একটি রিনিউয়াল ইভেন্ট (উদাহরণ: invoice.paid) পাঠায়। আপনার সার্ভার এটিকে গ্রহণ করে, ডাটাবেস আপডেট করে, কিন্তু 200 রিটার্ন করতে দেরি করে (cold start, ব্যস্ত ডাটাবেস)। Stripe ধরে নেয় যে এটি ব্যর্থ হয়েছে এবং একই ইভেন্টটি পুনরায় পাঠায়।

প্রথম ডেলিভারিতে আপনি অ্যাক্সেস গ্রান্ট করেন। রিট্রাইতে আপনি এটি একই ইভেন্ট হিসেবে শনাক্ত করে কিছু করবেন না। পরে, একটি রিফান্ড ইভেন্ট আসে (উদাহরণ: charge.refunded) এবং আপনি একবার অ্যাক্সেস রিভোক করেন।

ডাটাবেসে স্টেট মডেল করার একটি সহজ উপায় (AppMaster Data Designer-এ বানানোর মতো টেবিলগুলো):

  • webhook_events(event_id UNIQUE, type, processed_at, status)
  • invoices(invoice_id UNIQUE, subscription_id, status, paid_at, refunded_at)
  • entitlements(customer_id, product, active, valid_until, source_invoice_id)

প্রতিটি ইভেন্টের পরে ডাটাবেস কেমন দেখা উচিত

After Event A (renewal, first delivery): webhook_events-এ event_id=evt_123 নামে একটি নতুন সারি status=processed সহ যোগ হবে। invoices পেইড হিসেবে চিহ্নিত হবে। entitlements.active=true হবে এবং valid_until এক বিলিং পিরিয়ড আগায় যাবে।

After Event A again (renewal, retry): webhook_events-এ ইনসার্ট ব্যর্থ হবে (unique event_id) অথবা আপনার হ্যান্ডলার দেখে নেবে এটি আগে থেকেই প্রসেসড। ইনভয়েস বা entitlements-এ কোনো পরিবর্তন হবে না।

After Event B (refund): একটি নতুন webhook_events সারি event_id=evt_456 হবে। invoices.refunded_at সেট হবে এবং status=refunded হবে। entitlements.active=false (অথবা valid_until এখন করা হবে) source_invoice_id ব্যবহার করে সঠিকভাবে রিভোক করা হবে।

গুরুত্বপূর্ণ বিষয় হল টাইমিং: ডেডুপ চেক যেকোন গ্রান্ট বা রিভোক লেখার আগে হয়।

লঞ্চের আগে দ্রুত চেকলিস্ট

নিরাপদ পেমেন্ট ওয়েবহুক তৈরি করুন
Dedup টেবিল ও স্পষ্ট প্রসেস ফ্লো নিয়ে একটি idempotent webhook হ্যান্ডলার তৈরি করুন।
এখন চেষ্টা করুন

লাইভ ওয়েবহুক চালুর আগে আপনি প্রমাণ চান যে এক অভিজ্ঞ ইভেন্ট দিয়ে বিলিং রেকর্ড ঠিক একবারই আপডেট হচ্ছে, এমনকি প্রোভাইডার এটি দুবার (বা দশবার) পাঠালেও।

এন্ড-টু-এন্ড আপনার সেটআপ ভ্যালিডেট করতে এই চেকলিস্ট ব্যবহার করুন:

  • নিশ্চিত করুন প্রতিটি ইনকামিং ইভেন্ট প্রথমেই সংরক্ষিত হচ্ছে (raw payload, event id, type, created time, এবং signature verification ফলাফল), এমনকি পরে ধাপগুলো ব্যর্থ হলে থেকেও।
  • যাচাই করুন ডুপ্লিকেটগুলো দ্রুত শনাক্ত হচ্ছে (একই provider event id) এবং হ্যান্ডলার ইনভয়েস, সাবস্ক্রিপশন, বা entitlement পরিবর্তন না করে দ্রুত বেরিয়ে যাচ্ছে।
  • প্রমাণ করুন যে বিজনেস আপডেটগুলো একবারই: এক ইনভয়েস স্ট্যাটাস চেঞ্জ, এক সাবস্ক্রিপশন স্টেট চেঞ্জ, এক entitlement গ্রান্ট বা রিভোক।
  • নিশ্চিত করুন ব্যর্থতা পর্যাপ্ত বিবরণ সহ রেকর্ড হচ্ছে যাতে সেগুলো নিরাপদে রিপ্লে করা যায় (এরর মেসেজ, কোন ধাপে ব্যর্থ, রিট্রাই স্ট্যাটাস)।
  • টেস্ট করুন হ্যান্ডলার দ্রুত রেসপন্স দেয়: রিসিভ করার পরেই acknowedge করুন, এবং অনুরোধের ভেতরে ধীর কাজ করা এড়িয়ে চলুন।

শুরুতে একটি বড় অবজারভেবিলিটি সেটআপের দরকার নেই, কিন্তু কিছু সিগন্যাল দরকার। লগ বা সরল ড্যাশবোর্ড থেকে এগুলো ট্র্যাক করুন:

  • ডুপ্লিকেট ডেলিভারির স্পাইক (প্রায়ই স্বাভাবিক, কিন্তু বড় ঝাঁক কিছু টাইমআউট বা প্রোভাইডার সমস্যার ইঙ্গিত হতে পারে)
  • ইভেন্ট টাইপ অনুযায়ী উচ্চ এরর রেট (যেমন invoice payment failed)
  • ব্যাকলগ বাড়ছে এমন ইভেন্টগুলোর সংখ্যা যেগুলো রিট্রাইতে আটকে আছে
  • মিসম্যাচ চেক (পেইড ইনভয়েস কিন্তু মিসিং entitlement, revoked subscription কিন্তু অ্যাক্সেস এখনও আছে)
  • প্রসেসিং টাইম দ্রুত বাড়া

AppMaster-এ কাজ করলে ইভেন্ট স্টোরেজকে Data Designer-এ একটি ডেডিকেটেড টেবিলে রাখুন এবং "mark processed"-কে আপনার Business Process-এ একটি একক, অ্যাটমিক ডিসিশন পয়েন্ট বানান।

পরবর্তী ধাপ: টেস্ট, মনিটর, এবং নো-কোড ব্যাকএন্ডে তৈরি করা

টেস্টিং হল যেখানে idempotency নিজেকে প্রমাণ করে। শুধু হ্যাপি-পাথই চালাবেন না। একই ইভেন্ট কয়েকবার রিপ্লে করুন, ইভেন্টগুলো আউট অব অর্ডারে পাঠান, এবং টাইমআউট জোর করে প্রোভাইডার রিট্রাই করানোর চেষ্টা করুন। দ্বিতীয়, তৃতীয়, এবং দশম ডেলিভারি কিছুই পরিবর্তন করা উচিত নয়।

প্রাথমিকভাবে ব্যাকফিলিংর প্ল্যান রাখুন। এক সময় আপনি পূর্বের ইভেন্টগুলো রিপ্রসেস করতে চাইতে পারেন বগ ফিক্স, স্কিমা চেঞ্জ, বা প্রোভাইডার ইন্সিডেন্টের পরে। যদি আপনার হ্যান্ডলার প্রকৃতপক্ষে idempotent হয়, ব্যাকফিলিং হয়ে যায় "একই পাইপলাইনের মাধ্যমে ইভেন্টগুলো রিপ্লে করা" ব্যতীত ডুপ্লিকেট তৈরি না করা।

সাপোর্ট টিমের জন্য একটি ছোট রানবুক রাখুন যাতে সমস্যাগুলো আন্দাজে পরিণত না হয়:

  • ইভেন্ট ID খুঁজুন এবং পরীক্ষা করুন এটি প্রসেসড হিসেবে রেকর্ড আছে কি না।
  • ইনভয়েস বা সাবস্ক্রিপশন রেকর্ড চেক করে প্রত্যাশিত স্টেট ও টাইমস্ট্যাম্প নিশ্চিত করুন।
  • entitlement রেকর্ড দেখুন (কেন, কখন, এবং কোন ইভেন্ট ID-তে অ্যাক্সেস গ্রান্ট করা হয়েছিল)।
  • প্রয়োজনে সেই একক ইভেন্ট ID-এর জন্য সেফ রি-প্রসেস মোডে প্রসেস পুনরায় চালান।
  • যদি ডেটা inconsistent হয়, এক corrective action করুন এবং সেটি রেকর্ড করুন।

যদি আপনি এটাকে অনেক বয়লারপ্লেট ছাড়াই বাস্তবায়ন করতে চান, AppMaster (appmaster.io) আপনাকে কোর টেবিলগুলো মডেল করতে দেয় এবং একটি ভিজ্যুয়াল Business Process-এ ওয়েবহুক ফ্লো তৈরি করতে দেয়, একই সময়ে পেছন থেকে বাস্তব সোর্স কোড জেনারেট করে।

লাইভ ট্রাফিক ও রেভিনিউ স্কেলে করার আগে এন্ড টু এন্ড ওয়েবহুক হ্যান্ডলার বানিয়ে রিট্রাই-এর অধীনে এটা সেফ রাখুন।

প্রশ্নোত্তর

কেন আমার পেমেন্ট প্রোভাইডার একই ওয়েবহুক একাধিকবার পাঠায়?

ডুপ্লিকেট ওয়েবহুক ডেলিভারি স্বাভাবিক — প্রোভাইডাররা at least once ডেলিভারির দিকে অপ্টিমাইজ করে। যদি আপনার এন্ডপয়েন্ট টাইমআউট করে, 5xx রিটার্ন করে, বা সংযোগ খানিকক্ষণ পড়ে যায়, প্রোভাইডার একই ইভেন্টটি তখন পুনরায় পাঠায় যতক্ষণ না তারা সফল রেসপন্স পায়।

ওয়েবহুক ইভেন্ট ডেডুপ করার সেরা উপায় কী?

প্রোভাইডারের ইউনিক event ID (ওয়েবহুক ইভেন্ট আইডেন্টিফায়ার) ব্যবহার করুন, নয়তো ইনভয়েস অ্যামাউন্ট, টাইমস্ট্যাম্প বা কাস্টমার ইমেইল দিয়ে ডেডুপ করা ঠিক নয়। সেই event ID-টি একটি unique constraint-সহ সংরক্ষণ করুন যাতে রিট্রাই সহজেই শনাক্ত করে নিরাপদে উপেক্ষা করা যায়।

আপডেট করার আগে কি আমি ইভেন্টটি সংরক্ষণ করা উচিত?

ইনসার্ট ইভেন্ট রেকর্ডটা প্রথমে করুন, তারপর invoices, subscriptions, বা entitlements আপডেট করুন। যদি ইনসার্ট ব্যর্থ হয় কারণ event ID ইতিমধ্যে আছে, তাহলে প্রসেস বন্ধ করে সাফল্য রিটার্ন করুন যাতে রিট্রাইগুলি ডাবল আপডেট তৈরি না করে।

ওয়েবহুক ডেডুপ রেকর্ডগুলো কতক্ষণ রাখা উচিত?

দেরি হওয়া রিট্রাই কভার করার এবং তদন্ত সমর্থনের জন্য সেগুলো যথেষ্ট সময় ধরে রাখুন। সাধারণ ডিফল্ট হলো 30–90 দিন, আর যদি বিতর্ক, চার্জব্যাক বা দীর্ঘ সাবস্ক্রিপশন চক্র থাকে তাহলে (উদাহরণস্বরূপ) 6–12 মাস। পরবর্তী সময়ে পুরোনো রেকর্ডগুলো পিউর্জ করে টেবিল দ্রুত রাখুন।

যদি আমি ইতিমধ্যে ডেডুপ করি তাহলে কি সত্যিই সিগনেচার যাচাই দরকার?

হ্যাঁ — বিলিং ডেটা স্পর্শ করার আগে সিগনেচার যাচাই করুন, তারপর পার্স ও ভ্যালিডেট করুন। যদি সিগনেচার ভেরিফিকেশন ব্যর্থ হয়, রিকোয়েস্টটি বাতিল করুন এবং বিলিং চেঞ্জ না লিখুন — কারণ ডেডুপিং আপনাকে নকল “paid” ইভেন্ট থেকে রক্ষা করবে না।

কিভাবে ওয়েবহুক টাইমআউট হ্যান্ডল করব যাতে ডুপ্লিকেট না হয়?

ইভেন্টটি নিরাপদে সংরক্ষিত হওয়ার পর দ্রুত গ্রহণ করা পছন্দ করুন, এবং ভারী কাজ ব্যাকগ্রাউন্ডে পাঠান। ধীর হ্যান্ডলার টাইমআউট বাড়ায়, যেটা আরও রিট্রাই ট্রিগার করে এবং ডুপ্লিকেট আপডেটের সম্ভাবনা বাড়ায় যদি কিছু সম্পূর্ণভাবে idempotent না হয়।

ইভেন্টগুলো যদি আউট অব অর্ডারে পৌঁছে তাহলে আমি কী করব?

শুধুমাত্র এমন পরিবর্তন প্রয়োগ করুন যা স্টেটকে আগেভাগেই এগিয়ে নিয়ে যায়, এবং স্টেইল ইভেন্টগুলো উপেক্ষা করুন। সম্ভাব্য হলে ইভেন্ট টাইমস্ট্যাম্প ব্যবহার করুন এবং একটি সাধারণ স্ট্যাটাস প্রায়োরিটি ব্যবহার করুন (যেমন, refunded কখনো paid দ্বারা ওভাররাইট হবেনা, canceled কখনো active দ্বারা ওভাররাইট হবেনা)।

কিভাবে রিনিউয়াল রিট্রাইয়ের সময় দুইবার অ্যাক্সেস গ্রান্ট হওয়া এড়াব?

প্রতিটি ইভেন্টে নতুন entitlement রো না তৈরির চেষ্টা করুন। "ensure one entitlement per user/product/period (or per subscription)" টাইপের একটি upsert নিয়ম ব্যবহার করুন, তারপর ডেট/লিমিট আপডেট করুন এবং কোন ইভেন্ট ID-তে পরিবর্তনটি ঘটল সেটা অডিটের জন্য রেকর্ড করুন।

কেন invoice এবং entitlement আপডেটগুলো এক ট্রানজেকশনে রাখা উচিত?

invoice, subscription, এবং entitlement পরিবর্তনগুলো একসাথে একটি ডাটাবেস ট্রানজেকশনে লিখুন যাতে সবগুলো একসাথে সফল বা ব্যর্থ হয়। এটা প্রতিরোধ করে split state — যেমন “invoice paid” কিন্তু “কোনো অ্যাক্সেস নেই”, অথবা “অ্যাক্সেস ছিন্ন” কিন্তু মিলতি রিফান্ড রেকর্ড নেই।

আমি কি AppMaster-এ কাস্টম ব্যাকএন্ড কোড ছাড়া এটা নিরাপদে বাস্তবায়ন করতে পারি?

হ্যাঁ। এটা উপযুক্ত: একটি WebhookEvents মডেল তৈরি করুন যার মধ্যে ইউনিক event ID থাকবে, তারপর একটি Business Process বানান যা "already seen?" চেক করে এবং আগে থাকলে বেরিয়ে যায়। ডেডুপ এবং রিপ্লে ডুপ তৈরি না করার জন্য invoices/subscriptions/entitlements স্পষ্টভাবে Data Designer-এ মডেল করুন।

শুরু করা সহজ
কিছু আশ্চর্যজনকতৈরি করুন

বিনামূল্যের পরিকল্পনা সহ অ্যাপমাস্টারের সাথে পরীক্ষা করুন।
আপনি যখন প্রস্তুত হবেন তখন আপনি সঠিক সদস্যতা বেছে নিতে পারেন৷

এবার শুরু করা যাক