HandyPay

API রেফারেন্স

TypeScript উদাহরণ সহ সম্পূর্ণ শেষ বিন্দু উল্লেখ। সমস্ত উদাহরণ ব্যবহার করে handypay থেকে সাহায্যকারী দ্রুত শুরুটুলিং ডাউনলোড করতে পারে OpenAPI 3.1 চুক্তি.

বেস URL

https://api.handypay.me/api/v1
bash

API সংস্করণঃ 2025-01-01 (ফিরে এসেছে X-API-Version প্রতিটি প্রতিক্রিয়ায় হেড)।

প্রমাণীকরণ

সমস্ত অনুরোধের জন্য একটি বাহক টোকেন প্রয়োজন Authorization হেডার।

Authorization: Bearer hp_live_your_api_key_here
javascript
  • কীগুলি এর সাথে যুক্ত করা হয়েছে hp_live_ উৎপাদনের জন্য।
  • কীগুলি এর সাথে যুক্ত করা হয়েছে hp_test_ sandbox/testing-এর জন্য।
  • থেকে কী তৈরি করুন এবং পরিচালনা করুন মার্চেন্ট পোর্টাল.

টেস্ট মোড সম্পূর্ণরূপে বিচ্ছিন্ন

প্রত্যেকটি hp_test_ অনুরোধ একটি নিবেদিত পরীক্ষার অ্যাকাউন্ট ব্যবহার করে। পণ্য, গ্রাহক, অর্থ প্রদান, সদস্যপদ এবং ওয়েবহুক এন্ডপয়েন্টগুলি কখনই সরাসরি ব্যবসায়িক কার্যকলাপে প্রদর্শিত হয় না। টেস্ট মোড কর্মক্ষেত্র.

পরীক্ষার অর্থপ্রদানের তালিকা করুন
curl https://api.handypay.me/api/v1/test-payments \
  -H "Authorization: Bearer hp_test_your_api_key_here"
bash

হারের সীমা

API কী-তে প্রতি ঘন্টায় 1,000 অনুরোধ। সীমা অতিক্রম করে ফেরত 429 সঙ্গে একটি Retry-After হেডার।

প্রতিক্রিয়া বিন্যাস

প্রতিটি প্রতিক্রিয়া একটি সাধারণ খামের মধ্যে মোড়ানো থাকে।

সাফল্য

JSON
{
  "success": true,
  "data": { ... },
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
json

ত্রুটি

JSON
{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "Name is required"
  },
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
json

পৃষ্ঠাংকন

সমস্ত তালিকা শেষ বিন্দুগুলি কার্সার-ভিত্তিক পৃষ্ঠাটি ব্যবহার করে।

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
limitnumberনা।প্রতি পৃষ্ঠায় আইটেম (1-100, ডিফল্ট 10)
starting_afterstringনা।পূর্ববর্তী পৃষ্ঠার শেষ আইটেমের ID

প্রতিক্রিয়া অন্তর্ভুক্ত has_more: true যখন অতিরিক্ত পৃষ্ঠা থাকে।

পণ্য

এককালীন ক্রয়ের জন্য পণ্য তৈরি করুন এবং পরিচালনা করুন।

পদ্ধতিপথ।বর্ণনা
POST/v1/productsএকটি পণ্য তৈরি করুন
GET/v1/productsপণ্যের তালিকা করুন
GET/v1/products/:idএকটি পণ্য পান
PUT/v1/products/:idএকটি পণ্য আপডেট করুন
DELETE/v1/products/:idএকটি পণ্য সংরক্ষণ করুন

একটি পণ্য তৈরি করুন

TypeScript
const product = await handypay("/products", {
  method: "POST",
  body: JSON.stringify({
    name: "Premium Plan",
    description: "Access to all features",
    price: {
      amount: 2999,
      currency: "usd",
    },
  }),
});

console.log(product.id); // "prod_abc123"
typescript
cURL উদাহরণ
cURL
curl -X POST https://api.handypay.me/api/v1/products \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium Plan",
    "description": "Access to all features",
    "price": {
      "amount": 2999,
      "currency": "usd"
    }
  }'
bash

অনুরোধ সংস্থা

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
namestringহ্যাঁ।পণ্যের নাম
descriptionstringনা।পণ্যের বিবরণ
imagesstring[]না।8 পর্যন্ত ছবি URLs
metadataobjectনা।কাস্টম কী-ভ্যালু জোড়া (50 পর্যন্ত)
activebooleanনা।ডিফল্ট সত্য
urlstringনা।আপনার সাইটে পণ্য পৃষ্ঠা URL
shippablebooleanনা।পণ্যের পাঠানোর প্রয়োজন আছে কি না
unit_labelstringনা।প্রতি-ইউনিট লেবেল (যেমন "আসন", "লাইসেন্স")
statement_descriptorstringনা।ব্যাঙ্ক স্টেটমেন্ট টেক্সট (সর্বোচ্চ 22 অক্ষর)
tax_codestringনা।Stripe কর কোড
price.amountnumberনা।সর্বনিম্ন মুদ্রা একক (সেন্ট)-এ মূল্য
price.currencystringনা।ISO 4217 মুদ্রা কোড (যেমন "ইউ. এস. ডি", "জে. এম. ডি")
price.tax_behaviorstringনা।অন্তর্ভুক্তিমূলক, একচেটিয়া বা অনির্দিষ্ট

গ্রাহকরা

পুনরাবৃত্তি ক্রয় এবং সাবস্ক্রিপশনের জন্য গ্রাহকের রেকর্ড পরিচালনা করুন।

পদ্ধতিপথ।বর্ণনা
POST/v1/customersএকজন গ্রাহক তৈরি করুন
GET/v1/customersগ্রাহকদের তালিকা করুন
GET/v1/customers/:idএকজন গ্রাহক পান।
PUT/v1/customers/:idএকজন গ্রাহককে আপডেট করুন
DELETE/v1/customers/:idএকজন গ্রাহককে মুছে ফেলুন

একজন গ্রাহক তৈরি করুন

TypeScript
const customer = await handypay("/customers", {
  method: "POST",
  body: JSON.stringify({
    email: "customer@example.com",
    name: "Jane Doe",
  }),
});

console.log(customer.id); // "cus_abc123"
typescript
cURL উদাহরণ
cURL
curl -X POST https://api.handypay.me/api/v1/customers \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "name": "Jane Doe"
  }'
bash

অনুরোধ সংস্থা

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
emailstringহ্যাঁ।গ্রাহকের ইমেল
namestringনা।গ্রাহকের নাম
phonestringনা।গ্রাহকের ফোন নম্বর
metadataobjectনা।কাস্টম কী-মান জোড়া

পেমেন্ট সেশন

Create hosted checkout sessions for one-time payments. Standard HandyPay pricing applies: 4.9% + US$0.40 per transaction on the Free and Brand plans, or 4.2% + US$0.40 on Pro. There is no extra API or platform fee on top.

পদ্ধতিপথ।বর্ণনা
POST/v1/payment-sessionsএকটি পেমেন্ট সেশন তৈরি করুন
GET/v1/payment-sessions/:idঅধিবেশনের অবস্থা পান
GET/v1/test-paymentsপরীক্ষার অর্থপ্রদানের তালিকা করুন (শুধুমাত্র hp_test_)

একটি পেমেন্ট সেশন তৈরি করুন (বিদ্যমান মূল্য সহ)

TypeScript
const session = await handypay("/payment-sessions", {
  method: "POST",
  body: JSON.stringify({
    line_items: [{ price_id: "price_abc123", quantity: 1 }],
    success_url: "https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}",
    cancel_url: "https://yoursite.com/cancel",
  }),
});

// Redirect customer to checkout
window.location.href = session.url;
typescript
cURL উদাহরণ
cURL
curl -X POST https://api.handypay.me/api/v1/payment-sessions \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "line_items": [{ "price_id": "price_abc123", "quantity": 1 }],
    "success_url": "https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://yoursite.com/cancel"
  }'
bash

একটি পেমেন্ট সেশন তৈরি করুন (কাস্টম পরিমাণ)

TypeScript
const session = await handypay("/payment-sessions", {
  method: "POST",
  body: JSON.stringify({
    line_items: [{
      amount: 5000,
      currency: "usd",
      name: "Custom Order",
      quantity: 1,
    }],
    success_url: "https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}",
    cancel_url: "https://yoursite.com/cancel",
  }),
});
typescript
cURL উদাহরণ
cURL
curl -X POST https://api.handypay.me/api/v1/payment-sessions \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "line_items": [{
      "amount": 5000,
      "currency": "usd",
      "name": "Custom Order",
      "quantity": 1
    }],
    "success_url": "https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://yoursite.com/cancel"
  }'
bash

অনুরোধ সংস্থা

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
line_itemsarrayহ্যাঁ।অন্তত একটি লাইনের জিনিস
success_urlstringহ্যাঁ।সফল অর্থপ্রদানের পর URL পুনর্নির্দেশ করুন
cancel_urlstringহ্যাঁ।গ্রাহক বাতিল করলে URL পুনর্নির্দেশ করুন
customer_idstringনা।বিদ্যমান গ্রাহক ID
customer_emailstringনা।ইমেল আগে থেকে পূরণ করুন (যদি না customer_id হয়)
pass_fees_to_customerbooleanনা।গ্রাহকের মোট প্রক্রিয়াকরণ এবং পরিষেবা ফি যোগ করুন
metadataobjectনা।কাস্টম কী-মান জোড়া
collect_shipping_addressbooleanনা।একটি পাঠানোর ঠিকানা সংগ্রহ করুন
billing_address_collectionstringনা।স্বয়ংক্রিয় বা প্রয়োজনীয়
shipping_countriesstring[]না।অনুমোদিত ISO-2 গন্তব্য দেশ
shipping_optionsarrayনা।ক্ষুদ্রতম মুদ্রা ইউনিটে লেবেল এবং পরিমাণ প্রেরণ করা হয়

লাইন আইটেম ক্ষেত্র

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
price_idstringনা।বিদ্যমান Stripe মূল্য ID
amountnumberনা।কাস্টম পরিমাণ সেন্ট
currencystringনা।পরিমাণের সঙ্গে প্রয়োজনীয়
namestringনা।পরিমাণের সঙ্গে প্রয়োজনীয়
quantitynumberহ্যাঁ।পরিমাণ

দুটোই দিন। price_id অথবা amount+currency+name প্রতি লাইন আইটেম।

Which line-item form should I use?

ব্যবহার করুন price_id line items to sell products from your HandyPay catalog, and amount + currency + name line items for amounts computed at request time. Both forms work for every HandyPay account: HandyPay decides where each session is hosted so card payments always work, with the same fees and payout flow as hosted payment links. Before August 29, 2026, accounts paid by cross-border payout could see "No valid payment method types for this Checkout Session" on price_id sessions; that is fixed, and no request changes are needed.

HandyPay বিকল্প {CHECKOUT_SESSION_ID} URL-এর সাফল্যে। জিজ্ঞাসা করুন যে ID একই বণিক এবং একই live/test কী মোডের সাথে যা এটি তৈরি করেছে। অধিবেশনটি শেষ হওয়ার পরেও প্রশ্নবিদ্ধ থাকে, তবে আপনার স্বাক্ষরিত ওয়েবহুকটি এখনও চালান পরিপূর্ণতা চালাতে হবে।

অন্তর্নির্মিত অর্থপ্রদান

আপনার নিজের চেকআউট পেজে Stripe উপাদানগুলি রেন্ডার করতে চাইলে আপনার সার্ভার থেকে একটি PaymentIntent তৈরি করুন। আপনার HandyPay API কী সার্ভারের পাশে থাকে; শুধুমাত্র ফেরত দেওয়া প্রকাশযোগ্য কী, সংযুক্ত অ্যাকাউন্ট ID এবং স্বল্পস্থায়ী ক্লায়েন্ট গোপন ব্রাউজারে পাঠান।

পদ্ধতিপথ।বর্ণনা
POST/v1/payment-intentsএকটি এম্বেডেড PaymentIntent তৈরি করুন
সার্ভার-সাইড TypeScript
const intent = await handypay("/payment-intents", {
  method: "POST",
  body: JSON.stringify({
    amount: 5000,
    currency: "ttd",
    description: "Order #1042",
    customer_email: "buyer@example.com",
    pass_fees_to_customer: true,
    metadata: { order_id: "1042" },
  }),
});

// Pass these values to Stripe.js/Elements. Never pass HANDYPAY_API_KEY.
return {
  clientSecret: intent.client_secret,
  publishableKey: intent.publishable_key,
  stripeAccount: intent.stripe_account,
};
typescript
মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
amountnumberহ্যাঁ।ক্ষুদ্রতম মুদ্রা একক-এ ধনাত্মক পূর্ণসংখ্যা
currencystringহ্যাঁ।ISO 4217 তিন অক্ষরের মুদ্রা কোড
descriptionstringনা।অর্থপ্রদানের বিবরণ
customer_emailstringনা।রসিদ এবং পুনর্মিলনের জন্য গ্রাহকের ইমেল
pass_fees_to_customerbooleanনা।অর্থের পরিমাণ বাড়িয়ে দিন যাতে গ্রাহক অর্থমূল্য বহন করতে পারেন।
metadataobjectনা।আপনার অর্ডার বা চালান শনাক্তকারী

টাকা ফেরত

প্রমিত বণিকের মালিকানাধীন অর্থ ফেরত দিন। HandyPay অর্থপ্রদানের মালিকানা এবং উপলব্ধ ব্যালেন্স যাচাই করে। বাদ দিন amount পূর্ণ অর্থ ফেরতের জন্য।

পদ্ধতিপথ।বর্ণনা
POST/v1/refundsএকটি পূর্ণ বা আংশিক ফেরত তৈরি করুন
TypeScript
const refund = await handypay("/refunds", {
  method: "POST",
  body: JSON.stringify({
    session_id: "cs_live_...",
    amount: 2500,
    reason: "requested_by_customer",
  }),
});

console.log(refund.id, refund.status);
typescript
মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
session_idstringশর্তসাপেক্ষ।চেকআউট সেশন ID (সি. এস...)। এটি বা payment_intent ব্যবহার করুন।
payment_intentstringশর্তসাপেক্ষ।PaymentIntent ID (পাই...)। এটি বা session_id ব্যবহার করুন।
amountnumberনা।ক্ষুদ্রতম মুদ্রা ইউনিটে আংশিক অর্থ ফেরতের পরিমাণ
reasonstringনা।নকল, প্রতারণামূলক, বা requested_by_customer

একটি বিতর্কিত, সম্পূর্ণরূপে ফেরত, আন্তঃ-বাণিজ্য বা অপর্যাপ্ত-ভারসাম্য প্রদান ফেরত তৈরি না করেই প্রত্যাখ্যান করা হয়।

বিবাদ।

সংযুক্ত বণিকের জন্য চার্জব্যাক পর্যালোচনা করুন এবং নির্ধারিত তারিখের আগে প্রমাণ প্রদান করুন।

পদ্ধতিপথ।বর্ণনা
GET/v1/disputesবিবাদের তালিকা তৈরি করুন
GET/v1/disputes/:idএকটি বিতর্ক এবং তার প্রমাণের অবস্থা পান
POST/v1/disputes/:id/evidenceপ্রমাণ আপডেট করুন বা জমা দিন
TypeScript
const dispute = await handypay("/disputes/dp_123/evidence", {
  method: "POST",
  body: JSON.stringify({
    evidence: {
      customer_email_address: "buyer@example.com",
      product_description: "Annual software subscription",
      customer_communication: "https://files.example.com/evidence/1042.pdf",
    },
    submit: false,
  }),
});
typescript
মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
evidenceobjectনা।Stripe স্ট্রিং মান হিসাবে বিরোধ প্রমাণ ক্ষেত্র
submitbooleanনা।প্রমাণ সম্পূর্ণ হলে এবং পর্যালোচনার জন্য প্রস্তুত থাকলেই সত্য নির্ধারণ করুন।

সাবস্ক্রিপশন

পুনরাবৃত্ত পণ্য তৈরি করুন এবং সাবস্ক্রিপশন পরিচালনা করুন।

সাবস্ক্রিপশন পণ্য

পদ্ধতিপথ।বর্ণনা
POST/v1/subscription-productsএকটি সাবস্ক্রিপশন পণ্য তৈরি করুন
GET/v1/subscription-productsসাবস্ক্রিপশন পণ্যের তালিকা করুন

সাবস্ক্রিপশন সেশন ও ব্যবস্থাপনা

পদ্ধতিপথ।বর্ণনা
POST/v1/subscription-sessionsসাবস্ক্রিপশন চেকআউট তৈরি করুন
GET/v1/subscriptionsসক্রিয় সদস্যপদগুলির তালিকা করুন
PATCH/v1/subscriptions/:id/quantityস্পষ্টভাবে আসন পরিবর্তন করুন
POST/v1/subscriptions/:id/cancelবিলিং সময়কাল শেষে বাতিল করুন

Choose the right session form

Both forms work for every HandyPay account: HandyPay decides where the checkout is hosted so card payments always work, exactly like hosted subscription links. Use a price_id to sell a subscription product from your catalog; this form also supports trials, fee passing, and saved customers. Use the inline form (amount_cents + currency + interval) when the amount or schedule is computed at request time. Before August 29, 2026, accounts paid by cross-border payout could see "No valid payment method types for this Checkout Session" on price_id sessions; that is fixed, and no request changes are needed.

সমর্থিত বিলিং ব্যবধান

weeklybi-weeklymonthlybi-monthlyquarterlysemi-annualannual

একটি সাবস্ক্রিপশন পণ্য তৈরি করুন

TypeScript
const subProduct = await handypay("/subscription-products", {
  method: "POST",
  body: JSON.stringify({
    name: "Pro Plan",
    description: "Monthly pro access",
    amount: 1999,
    currency: "usd",
    interval: "monthly",
    trial_period_days: 14,
  }),
});

console.log(subProduct.price.id); // Use this price_id for subscription sessions
typescript
cURL উদাহরণ
cURL
curl -X POST https://api.handypay.me/api/v1/subscription-products \
  -H "Authorization: Bearer hp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pro Plan",
    "description": "Monthly pro access",
    "amount": 1999,
    "currency": "usd",
    "interval": "monthly",
    "trial_period_days": 14
  }'
bash

অনুরোধ সংস্থা

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
namestringহ্যাঁ।পণ্যের নাম
descriptionstringনা।পণ্যের বিবরণ
amountnumberহ্যাঁ।সর্বনিম্ন মুদ্রা ইউনিটে মূল্য
currencystringহ্যাঁ।ISO 4217 মুদ্রা কোড
intervalstringহ্যাঁ।সমর্থিত ব্যবধানগুলির মধ্যে একটি
trial_period_daysnumberনা।কয়েক দিনের মধ্যে বিনামূল্যে ট্রায়াল
metadataobjectনা।কাস্টম কী-মান জোড়া

Create checkout with a saved price

TypeScript
const session = await handypay("/subscription-sessions", {
  method: "POST",
  body: JSON.stringify({
    price_id: subProduct.price.id,
    quantity: 5,
    customer_email: signedInUser.email, // Prefills the subscriber's email
    trial_period_days: 14,
    success_url: "https://example.com/success",
    cancel_url: "https://example.com/plans",
  }),
});

// Redirect the customer to session.url
typescript

trial_period_days, pass_fees_to_customer, এবং customer_id apply only to price_id sessions.

Prefill the subscriber's email

For API-created sessions, send customer_email from your server. Checkout opens with the address already filled, and the same address is used for receipts. If you provide customer_id, HandyPay uses that existing customer instead. Keep your HandyPay API key on the server.

Reusable hosted subscription links can also prefill email without an API request. Add the email query parameter with URLSearchParams before redirecting the customer:

TypeScript
const checkoutUrl = new URL("YOUR_HANDYPAY_SUBSCRIPTION_LINK");

// URLSearchParams safely encodes the email address.
checkoutUrl.searchParams.set("email", signedInUser.email);

window.location.assign(checkoutUrl.toString());
typescript

Prefer server-created sessions when possible. Query parameters can appear in browser history and logs, so only add an email address the customer has already provided to your application.

Create a subscription checkout (inline pricing)

TypeScript
// Inline pricing - use when the amount or schedule is computed at
// request time instead of coming from a saved subscription product.
const session = await handypay("/subscription-sessions", {
  method: "POST",
  body: JSON.stringify({
    amount_cents: 1999,
    currency: "ttd",
    interval: "month",       // day | week | month | year
    interval_count: 1,       // e.g. 3 with "month" = quarterly
    name: "Pro Plan",
    quantity: 1,
    customer_email: signedInUser.email, // Prefills the subscriber's email
    success_url: "https://example.com/success?session_id={CHECKOUT_SESSION_ID}",
    cancel_url: "https://example.com/plans",
  }),
});
typescript

Request body (inline form)

মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
amount_centsnumberহ্যাঁ।Recurring price in the smallest currency unit
currencystringহ্যাঁ।ISO 4217 code. USD, TTD, JMD, XCD, and other supported settlement currencies
intervalstringহ্যাঁ।day, week, month, or year
interval_countnumberনা।Billing every N intervals, 1-52 (default 1)
namestringনা।Product name shown at checkout
quantitynumberনা।Seats from 1 to 1,000 (default 1)
customer_emailstringনা।Pre-fill the customer email
success_urlstringহ্যাঁ।Redirect URL after checkout
cancel_urlstringহ্যাঁ।Redirect URL if the customer cancels
metadataobjectনা।Custom key-value pairs, echoed on webhooks

সক্রিয় সদস্যপদের আসন পরিবর্তন করুন

পরিমাণ অবশ্যই 1 থেকে 1,000 পর্যন্ত একটি পূর্ণসংখ্যা হতে হবে। বিলিং সমন্বয়টি একটি অন্তর্নিহিত ডিফল্টের উপর নির্ভর করার পরিবর্তে কীভাবে পরিচালনা করা হয় তা বেছে নিন।

TypeScript
const updated = await handypay("/subscriptions/sub_123/quantity", {
  method: "PATCH",
  body: JSON.stringify({
    quantity: 8,
    proration_behavior: "create_prorations",
  }),
});
typescript
মাঠটাইপ করুনপ্রয়োজন।বর্ণনা
quantitynumberহ্যাঁ।নতুন আসন গণনা 1 থেকে 1,000 পর্যন্ত
proration_behaviorstringনা।create_prorations, always_invoice, অথবা কিছুই নয়
item_idstringনা।নির্দিষ্ট সাবস্ক্রিপশন আইটেম যখন একটি সাবস্ক্রিপশনে একাধিক পণ্য থাকে

Webhooks

আপনার শেষ পয়েন্টগুলিতে HTTP POST-এর মাধ্যমে রিয়েল-টাইম ইভেন্ট বিজ্ঞপ্তিগুলি পান।

পদ্ধতিপথ।বর্ণনা
POST/v1/webhook-endpointsএকটি শেষ বিন্দু নিবন্ধিত করুন
GET/v1/webhook-endpointsশেষ পয়েন্টের তালিকা করুন
DELETE/v1/webhook-endpoints/:idএকটি শেষ বিন্দু নিষ্ক্রিয় করুন
  • শেষ পয়েন্টগুলিকে অবশ্যই HTTPS ব্যবহার করতে হবে।
  • Webhook শেষ পয়েন্টগুলি একটি নিবন্ধিত hp_test_ কী শুধুমাত্র পরীক্ষার ইভেন্টগুলি গ্রহণ করে। লাইভ এবং পরীক্ষার গন্তব্যগুলি আলাদাভাবে সংরক্ষণ করা হয়।
  • একটি ইভেন্টে সাবস্ক্রাইব করা প্রতিটি সক্রিয় এন্ডপয়েন্ট সেই এন্ডপয়েন্টের নিজস্ব গোপনীয়তার সাথে স্বাক্ষরিত একটি স্বাধীন ডেলিভারি পায়। একটি এন্ডপয়েন্টের গোপন বিষয়টিকে অন্যটির জন্য পুনরায় ব্যবহার করবেন না।
  • 10 সেকেন্ডের মধ্যে 2xx প্রতিক্রিয়া প্রদান করুন। প্রতিটি ইভেন্ট সংরক্ষণ করুন। id পার্শ্বপ্রতিক্রিয়ার আগে তাই ডুপ্লিকেট ডেলিভারি নিরাপদ।
  • 10 পরপর ডেলিভারি ব্যর্থতার পর, একটি শেষ বিন্দু স্বয়ংক্রিয়ভাবে নিষ্ক্রিয় হয়ে যায়।

সমর্থিত অনুষ্ঠানের প্রকার

  • payment_intent.succeeded
  • payment_intent.payment_failed
  • checkout.session.completed
  • checkout.session.expired
  • checkout.session.async_payment_succeeded
  • checkout.session.async_payment_failed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • charge.refunded
  • charge.dispute.created
  • charge.dispute.closed
স্বাক্ষরিত ওয়েবহুকগুলিকে সত্যের অর্থপ্রদানের উৎস হিসাবে বিবেচনা করুন-ব্রাউজারের সাফল্য পুনর্নির্দেশ নয়। payment_intent.payment_failed ঘটনা PaymentIntent কে চিহ্নিত করে data.idআপনার মেটাডেটা ব্যবহার করুন (উদাহরণস্বরূপ) order_id) এর মধ্যে সমন্বয় করতে। checkout.session.expired বিলম্বিত অর্থপ্রদান পদ্ধতির জন্য অধিবেশন মেয়াদ শেষ এবং অ্যাসিঙ্ক ইভেন্টগুলির জন্য।

ওয়েবহুকে স্বাক্ষর যাচাই করা হচ্ছে

প্রতিটি ডেলিভারিতে একটি X-HandyPay-Signature ফরম্যাটে হেডার sha256={hex}আপনার শেষ বিন্দুর স্বাক্ষরের গোপন তথ্য ব্যবহার করে র অনুরোধ সংস্থার HMAC-SHA256 গণনা করে যাচাই করুনঃ

TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";

function verifySignature(
  payload: string | Buffer,
  secret: string,
  signature: string
): boolean {
  const prefix = "sha256=";
  if (!signature.startsWith(prefix)) return false;
  const hex = signature.slice(prefix.length);
  if (!/^[0-9a-f]{64}$/i.test(hex)) return false;

  const expected = createHmac("sha256", secret).update(payload).digest();
  const received = Buffer.from(hex, "hex");
  return received.length === expected.length && timingSafeEqual(received, expected);
}
typescript

Next.js API রুট হ্যান্ডলার

app/api/webhooks/handypay/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createHmac, timingSafeEqual } from "node:crypto";

export const runtime = "nodejs";
const SECRET = process.env.HANDYPAY_WEBHOOK_SECRET;
if (!SECRET) throw new Error("HANDYPAY_WEBHOOK_SECRET is not configured");

function hasValidSignature(payload: string, signature: string): boolean {
  const prefix = "sha256=";
  if (!signature.startsWith(prefix)) return false;
  const hex = signature.slice(prefix.length);
  if (!/^[0-9a-f]{64}$/i.test(hex)) return false;
  const expected = createHmac("sha256", SECRET).update(payload).digest();
  const received = Buffer.from(hex, "hex");
  return received.length === expected.length && timingSafeEqual(received, expected);
}

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get("x-handypay-signature") ?? "";
  if (!hasValidSignature(body, signature)) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  let event: { id: string; type: string; data: unknown };
  try {
    event = JSON.parse(body);
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }

  // Persist event.id before side effects so redeliveries can be ignored safely.
  switch (event.type) {
    case "checkout.session.completed":
      // Fulfill an immediate payment.
      break;
    case "checkout.session.async_payment_succeeded":
      // Fulfill a delayed payment.
      break;
    case "charge.refunded":
      // Mark the matching order as refunded.
      break;
  }

  return NextResponse.json({ received: true });
}
typescript
Express.js হ্যান্ডলার
routes/webhooks.ts
// Register this route BEFORE app.use(express.json()).
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";

const router = express.Router();
const SECRET = process.env.HANDYPAY_WEBHOOK_SECRET;
if (!SECRET) throw new Error("HANDYPAY_WEBHOOK_SECRET is not configured");

function hasValidSignature(payload: Buffer, signature: string): boolean {
  const prefix = "sha256=";
  if (!signature.startsWith(prefix)) return false;
  const hex = signature.slice(prefix.length);
  if (!/^[0-9a-f]{64}$/i.test(hex)) return false;
  const expected = createHmac("sha256", SECRET).update(payload).digest();
  const received = Buffer.from(hex, "hex");
  return received.length === expected.length && timingSafeEqual(received, expected);
}

router.post(
  "/handypay",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = String(req.headers["x-handypay-signature"] ?? "");
    if (!hasValidSignature(req.body, signature)) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const event = JSON.parse(req.body.toString("utf8"));
    // Persist event.id before side effects so redeliveries are idempotent.
    console.log("Verified HandyPay event", event.id, event.type);
    return res.json({ received: true });
  }
);

export default router;
typescript

Webhook পেলোড বিন্যাস

JSON
{
  "id": "evt_abc123",
  "type": "payment_intent.succeeded",
  "created": 1706745600,
  "data": { ... }
}
json

অ্যাকাউন্ট

সংযুক্ত বণিকের চার্জ এবং পেআউট প্রস্তুতির পাশাপাশি ডিফল্ট পেআউট ব্যাঙ্ক অ্যাকাউন্ট পড়ুন। ব্যাঙ্ক এবং রাউটিং নম্বরগুলি মুখোশযুক্ত; শুধুমাত্র তাদের শেষ চারটি অঙ্ক ফেরত দেওয়া হয়।

পদ্ধতিপথ।বর্ণনা
GET/v1/accountসংযুক্ত অ্যাকাউন্ট এবং ছদ্মবেশী অর্থপ্রদানের বিবরণ পান
TypeScript
const account = await handypay("/account");

console.log({
  chargesEnabled: account.chargesEnabled,
  payoutsEnabled: account.payoutsEnabled,
  bank: account.bankAccount?.bankName,
  last4: account.bankAccount?.last4,
});
typescript

ত্রুটি কোড

কোডHTTPবর্ণনা
unauthorized401API কী অনুপস্থিত বা অবৈধ
key_revoked401API কী বাতিল করা হয়েছে।
key_expired401API কী-এর মেয়াদ শেষ হয়ে গেছে।
rate_limit_exceeded429অনেক অনুরোধ
validation_error400অনুরোধের মূল বৈধতা ব্যর্থ হয়েছে
invalid_url400অবৈধ success_url বা cancel_url
invalid_interval400অসমর্থিত বিলিং ব্যবধান
product_not_found404পণ্যের অস্তিত্ব নেই
customer_not_found404গ্রাহক নেই
session_not_found404চেকআউট অধিবেশন বিদ্যমান নেই
payment_not_found404এই বণিকের অর্থের অভাব রয়েছে বা তার মালিকানা নেই।
subscription_not_found404সাবস্ক্রিপশন বিদ্যমান নেই
refund_not_allowed400অর্থ প্রদান বিতর্কিত এবং তা ফেরত দেওয়া যাবে না।
already_refunded400ইতিমধ্যেই সম্পূর্ণ টাকা ফেরত দেওয়া হয়েছে।
refund_amount_too_large400অবশিষ্ট ফেরতযোগ্য ব্যালেন্সের চেয়ে বেশি
insufficient_balance400উপলব্ধ ব্যালেন্স অর্থ ফেরত দিতে পারে না।
balance_verification_failed400ভারসাম্য মালিকানা বা প্রাপ্যতা যাচাই করা যায়নি
endpoint_not_found404অজানা API শেষ বিন্দু
stripe_error502Stripe API একটি ত্রুটি প্রদান করেছে
internal_error500সার্ভারে অপ্রত্যাশিত ত্রুটি
payload_too_large413অনুরোধের পরিমাণ 1 এম. বি অতিক্রম করেছে
prohibited_content400বিষয়বস্তু গ্রহণযোগ্য ব্যবহার নীতি লঙ্ঘন করে
webhook_url_must_be_https400Webhook URL-কে অবশ্যই HTTPS ব্যবহার করতে হবে।
key_creation_rate_exceeded429টাইম উইন্ডোতে অনেক বেশি কী তৈরি করা হয়েছে
max_keys_reached400সর্বাধিক সক্রিয় API কী পৌঁছয় (25)

নিরাপত্তা

  • রাখুন। hp_live_ এবং hp_test_ আপনার সার্ভারে কীগুলি রাখুন। এগুলি কখনই ব্রাউজার JavaScript, মোবাইল অ্যাপ, URLs, লগ বা সোর্স কন্ট্রোলে রাখবেন না।
  • সমস্ত প্রতিক্রিয়ার মধ্যে রয়েছে X-Content-Type-Options: nosniff, X-Frame-Options: DENY, এবং Strict-Transport-Security হেডার।
  • অনুরোধের মূল সীমাঃ 1 MiB, স্ট্রিম বা চ্যাঙ্কড অনুরোধ সহ।
  • Webhook শেষ বিন্দুগুলিকে অবশ্যই HTTPS ব্যবহার করতে হবে।
  • সঠিক কাঁচা বাইটের বিপরীতে ওয়েবহুক স্বাক্ষরগুলি যাচাই করুন এবং স্থির সময়ে পরিপাকের তুলনা করুন।
  • পণ্যের নাম এবং বিবরণ একটি নিষিদ্ধ বিষয়বস্তু ব্লকলিস্টের বিরুদ্ধে প্রদর্শিত হয়।
  • 5 + বিষয়বস্তু লঙ্ঘন 24 ঘন্টার মধ্যে আপনার API কী স্থগিত করে দেবে।

API কী-এর সীমা

  • প্রতি 10 মিনিটের উইন্ডোতে সর্বোচ্চ 3 কী তৈরি করা হয়েছে।
  • প্রতি 1-ঘন্টা উইন্ডোতে সর্বোচ্চ 10 কী তৈরি করা হয়েছে।
  • প্রতি বণিক সর্বোচ্চ 25 সক্রিয় কী।