← সব টিউটোরিয়াল

টিউটোরিয়াল 11 / 12

তোমার ক্রিপ্টো মার্কেটপ্লেস তৈরি করো।

এক কার্ট থেকে বিক্রেতার পেমেন্ট পর্যন্ত, ব্যবহারিক $300 উদাহরণ আর সার্ভার-সাইড কোডসহ।

শেষে যা পাবে

গ্রাহকের পেমেন্ট আর বিক্রেতার পেআউট না মিশিয়ে পরীক্ষা, হিসাব মেলানো আর অটোমেশন করা যায় এমন পেমেন্ট ইন্টিগ্রেশন।

1. কে কী সামলাবে ঠিক করো

দোকান তোমার ক্যাটালগ, বিক্রেতার অ্যাকাউন্ট, কার্ট, শিপিং আর অর্ডার পূরণ সামলায়। Wholly Crypto চেকআউট, পেমেন্ট যাচাই, বিক্রেতার বরাদ্দ আর অনুমোদিত পেআউট সামলায়। বিক্রেতার রেকর্ড বিক্রেতার লগইন নয়; বিদ্যমান শপ প্লাগইন নিজে থেকে বহু বিক্রেতার কার্ট ভাগ করে না।

  1. এক কার্ট
  2. গ্রাহকের এক ইনভয়েস
  3. যাচাই করা বিক্রেতার অংশ
  4. অনুমোদিত পেআউট

গ্রাহক আগে প্রজেক্ট ওয়ালেটে পেমেন্ট করে। কী তোমার নিয়ন্ত্রণে আর বিক্রেতার পাওনা ফান্ড তুমি রাখো। এটি সরাসরি গ্রাহক-থেকে-বিক্রেতা ভাগ বা বিক্রেতার জন্য নন-কাস্টডিয়াল সেবা নয়।

Marketplace Bitcoin মেইননেট আর সমর্থিত EVM কয়েন ও সাধারণ ERC-20 টোকেন সমর্থন করে। বিক্রেতা গ্রাহকের ব্যবহৃত অ্যাসেট আর নেটওয়ার্কেই পায়, প্রচলিত মুদ্রায় নিজে থেকে বদলায় না। গ্রহণের সব 30 চেইন Marketplace পেআউটে সমর্থিত নয়।

2. পরিমাণের হিসাব করো

তিন বিক্রেতা প্রত্যেকে $100-এর পণ্য বিক্রি করে। এই উদাহরণে স্টোর বা বিক্রেতা ওভাররাইড ছাড়া প্রজেক্ট কমিশন 4% দাও।

অংশমোটতোমার কমিশনবিক্রেতা পায়
প্রতি বিক্রেতা$100$4$96
তিনজন মিলে$300$12$288

এগুলো ইনভয়েসের লক করা কোটের সমমূল্য, ক্রিপ্টোতে দেওয়া হয়। ভবিষ্যতের ডলারের মূল্য নিশ্চিত নয়। সাধারণ 1% প্রসেসিং ফিতে এই $300 ইনভয়েসে একবার $3 প্রিপেইড ক্রেডিট লাগে, প্রতি বিক্রেতায় নয়। সেই ফি লাগলে $12 মোট কমিশন থেকে নেটওয়ার্ক খরচের আগে $9 থাকে।

Bitcoin ফি বা EVM গ্যাসের জন্য বাধামুক্ত নেটিভ কয়েন আলাদা রাখো। ফি দিয়ে বিক্রেতার সুরক্ষিত মূল পরিমাণ খরচ করা চলবে না। সাধারণ সুইপ Marketplace রিসিভিং অ্যাড্রেস খরচ করতে পারে না।

3. ওয়ালেট আর বিক্রেতা প্রস্তুত করো

  1. প্রজেক্ট → Marketplace → সেটিংসে Marketplace চালু করে স্টোর বেছে কমিশন দাও। সেটআপের সময় পেআউট আর অটো নীতি বন্ধ রাখো।
  2. প্রজেক্ট ওয়ালেট আর ডেটাবেস ব্যাকআপ করো। স্টোরে দরকারি BTC/EVM পদ্ধতি চালু করো, স্ক্যানারের প্রস্তুতি দেখো আর প্রসেসিং ক্রেডিটের সঙ্গে আলাদা নেটিভ ফি রাখো।
  3. প্রতি বিক্রেতা যোগ করো। দোকানের বিক্রেতা ID-এর সঙ্গে তার UUID সেভ করো; external_id-তে সেই রেফারেন্স রাখতে পারো। সঠিক চেইন আর নেটওয়ার্কে প্রতিটি পেআউট অ্যাড্রেস স্বাধীনভাবে যাচাই করে অনুমোদন করো।

এক চেকআউট পদ্ধতিতে অংশ নেওয়া প্রতিটি বিক্রেতার মেলা অনুমোদিত গন্তব্য লাগে। পরে বিক্রেতার অ্যাড্রেস বদলালে পুরোনো পাওনা নীরবে নতুন গন্তব্যে যায় না।

চেকআউটে শূন্য কনফার্মেশন বা এক স্ক্যানার চললেও পেআউট যাচাইয়ে শূন্যের বেশি কনফার্মেশন আর দুই স্বাধীন সামঞ্জস্যপূর্ণ সেবাদাতা লাগে।

কনসোল সেটআপের ধাপ · ব্যাকআপ আর পুনরুদ্ধার

4. এক কার্ট যুক্ত করো

সেটিংস → API অ্যাক্সেসে প্রজেক্ট-সীমিত Marketplace ক্রেডেনশিয়াল বানাও। চেকআউট ব্যাকএন্ডকে marketplace.read আর invoices.write দাও, প্রয়োজনমতো স্টোরে সীমিত করে। অ্যাড্রেস আর পেআউট অনুমোদনের অনুমতি সেই কীতে রেখো না।

সার্ভারে দাম, ছাড়, কর আর শিপিং হিসাব করে বিক্রেতার অংশে বরাদ্দ করো। ধনাত্মক দশমিক স্ট্রিং দিয়ে 1–100 আলাদা বিক্রেতা পাঠাও; তাদের মোট অংশ ইনভয়েসের সঙ্গে হুবহু মিলতে হবে। ব্রাউজারের ভাগে কখনো ভরসা কোরো না বা টাকায় ফ্লোটিং-পয়েন্ট হিসাব কোরো না।

নিচে API হোস্টনেম আর UUID প্লেসহোল্ডার বদলাও। সার্ভার এনভায়রনমেন্ট থেকে WHOLLY_TOKEN নাও। অনুরোধ কনফিগার করা 4% কমিশন নেয়; কমিশন ওভাররাইডের অনুমতি লাগে না।

cURL, JavaScript, PHP বা Python-এ অনুরোধ খোলো
cURL
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
  --request POST \
  --url "https://api.example.com/v1/marketplace/invoices" \
  --header "Authorization: Bearer $WHOLLY_TOKEN" \
  --header 'Idempotency-Key: cart-1042-marketplace-v1' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "project_id": "YOUR_PROJECT_ID",
  "store_id": "YOUR_STORE_ID",
  "amount": "300.00",
  "currency": "USD",
  "order_id": "cart-1042",
  "description": "One order from three vendors",
  "ipn_url": "https://shop.example.com/payments/wholly-ipn",
  "metadata": {
    "cart_id": "cart-1042"
  },
  "allocations": [
    {
      "vendor_id": "VENDOR_A_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_B_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_C_UUID",
      "gross_amount": "100.00"
    }
  ]
}'
JavaScript
// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
  "project_id": "YOUR_PROJECT_ID",
  "store_id": "YOUR_STORE_ID",
  "amount": "300.00",
  "currency": "USD",
  "order_id": "cart-1042",
  "description": "One order from three vendors",
  "ipn_url": "https://shop.example.com/payments/wholly-ipn",
  "metadata": {
    "cart_id": "cart-1042"
  },
  "allocations": [
    {
      "vendor_id": "VENDOR_A_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_B_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_C_UUID",
      "gross_amount": "100.00"
    }
  ]
}`;
const response = await fetch("https://api.example.com/v1/marketplace/invoices", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`,
    "Idempotency-Key": "cart-1042-marketplace-v1",
    "Content-Type": "application/json"
  },
  body,
  redirect: "error",
  signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());
PHP
<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
  "project_id": "YOUR_PROJECT_ID",
  "store_id": "YOUR_STORE_ID",
  "amount": "300.00",
  "currency": "USD",
  "order_id": "cart-1042",
  "description": "One order from three vendors",
  "ipn_url": "https://shop.example.com/payments/wholly-ipn",
  "metadata": {
    "cart_id": "cart-1042"
  },
  "allocations": [
    {
      "vendor_id": "VENDOR_A_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_B_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_C_UUID",
      "gross_amount": "100.00"
    }
  ]
}
JSON;
$ch = curl_init("https://api.example.com/v1/marketplace/invoices");
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: cart-1042-marketplace-v1", "Content-Type: application/json"],
    CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));
Python
# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
    "Idempotency-Key": "cart-1042-marketplace-v1",
    "Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
  "project_id": "YOUR_PROJECT_ID",
  "store_id": "YOUR_STORE_ID",
  "amount": "300.00",
  "currency": "USD",
  "order_id": "cart-1042",
  "description": "One order from three vendors",
  "ipn_url": "https://shop.example.com/payments/wholly-ipn",
  "metadata": {
    "cart_id": "cart-1042"
  },
  "allocations": [
    {
      "vendor_id": "VENDOR_A_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_B_UUID",
      "gross_amount": "100.00"
    },
    {
      "vendor_id": "VENDOR_C_UUID",
      "gross_amount": "100.00"
    }
  ]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/marketplace/invoices",
                  method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
    print(json.load(response))

পাঠানোর আগে অনুরোধ বডি আর Idempotency-Key স্থায়ীভাবে সেভ করো। টাইমআউটে একই কী দিয়ে একই বডি আবার পাঠাও। অর্ডারের সঙ্গে data.invoice_id সেভ করে উপরের স্তরের links.checkout-এ রিডাইরেক্ট করো। গ্রাহকের ব্রাউজারে কখনো API কী রেখো না।

প্রজেক্ট আর স্টোর UUID কোথায় পাবে →

SDK চাই? Marketplace উদাহরণ দিয়ে শুরু করো:

5. পেমেন্ট আর পেআউট আলাদা ট্র্যাক করো

চেকআউট ফেরার URL পেমেন্টের প্রমাণ নয়। র বডিতে কলব্যাক স্বাক্ষর যাচাই করো, টাইমস্ট্যাম্প আর প্রত্যাশিত প্রজেক্ট দেখো, আর স্বীকার করার আগে event_id অনন্যভাবে সেভ করো। আলাদা অর্ডার-স্তরের সুরক্ষা রাখো, যাতে রিট্রাইয়ে দুবার অর্ডার পূরণ না হয়।

Eventকী জানায়
invoice.settledগ্রাহকের ইনভয়েস নিষ্পত্তি হয়েছে। অর্ডার পূরণের আগে পর্যালোচনার চিহ্ন আর Marketplace হোল্ড দেখো।
marketplace.allocations.availableযাচাই করা অংশ পেআউটের জন্য উপলব্ধ। তাতে কোনো বিক্রেতাকে ইতিমধ্যে দেওয়া হয়েছে এমন নয়।
marketplace.payout.confirmedপেআউটের কনফার্মেশন পরীক্ষা শেষ।

ইনভয়েস IPN স্টোরের IPN সিক্রেট ব্যবহার করে। Marketplace জীবনচক্রের ওয়েবহুক নিজের এন্ডপয়েন্ট স্বাক্ষরের সিক্রেট ব্যবহার করে। দুই রিসিভার আলাদা রাখো। হারানো বা এলোমেলো ইভেন্ট মেলাতে API দিয়ে বর্তমান ইনভয়েস বা পেআউট পড়ো।

ইনভয়েস কলব্যাক আর স্বাক্ষর যাচাই · Marketplace ইভেন্ট আর API রেফারেন্স

6. আগে পর্যালোচনা, পরে অটোমেশন

যাচাই করা অংশ উপলব্ধ হলে পেআউটের বিরতি সরাও, কিন্তু অটো নীতি বন্ধ রাখো। Marketplace → পেআউটে বরাদ্দ প্রস্তুত করে প্রাপক আর নেটিভ ফি/গ্যাস সীমা দেখো, তারপর সঠিক পরিকল্পনা একবার অনুমোদন করো। শুধু ব্রডকাস্ট নয়, পেইড হওয়া পর্যন্ত দেখো।

Bitcoin নির্বাচিত ইনভয়েসের সব অবৈতনিক অংশ এক ব্যাচে করে। EVM টোকেন পেআউটে টোকেন পাঠানোর আগে গ্যাস ফান্ডিং লাগতে পারে। এগুলো কয়েকটি লেনদেন, একসঙ্গে সব-না-হয়-কিছুই-না ভাগ নয়।

ছোট সফল পরীক্ষার পরে প্রতি অ্যাসেটে এক অটো নীতি দাও: ন্যূনতম পেআউট, প্রতি পেআউট ও দৈনিক মূল পরিমাণের সীমা, নেটিভ ফি/গ্যাস বাজেট, বিরতি আর কনফার্মেশন। চালু করলে আরও ক্লিক ছাড়া পাঠানোর অনুমতি হয়। কম ক্রেডিট বা বন্ধ সার্ভার ট্রান্সফার গেট তবু খরচ থামাতে পারে।

7. জটিল পরিস্থিতি সামলাও

কম, দেরিতে, মিশ্র বা রিঅর্গ হওয়া পেমেন্ট

নিষ্পত্তি হওয়া চেকআউট পুরো ফান্ড থাকা বিক্রেতার ভাগ নিশ্চিত করে না। টলারেন্স কম থাকা ফান্ড তৈরি করতে পারে না। আটকে থাকা বরাদ্দ দেখো, পেমেন্টের অপেক্ষা করো, রিফান্ড দাও বা ছোট সম্পূর্ণ ফান্ড থাকা ভাগ স্পষ্টভাবে অনুমোদন করো। বাড়তি পেমেন্ট নিজে থেকে বাড়তি মার্কেটপ্লেস আয় নয়।

Bitcoin, EVM coin বা supported ERC-20 token-এ overpayment quoted vendor payout আটকায় না। Vendor share অপরিবর্তিত, excess reconciliation-এর জন্য আলাদা থাকে।

পেআউট আটকে গেলে বা অনুরোধ টাইমআউট হলে

সেভ করা লেনদেন হ্যাশ, সোর্স ব্যালান্স, গ্যাস আর পর্যালোচনার কারণ দেখো। পুরোনো পেআউট চালাও বা হিসাব মেলাও; রেসপন্স হারিয়েছে বলে দ্বিতীয় ট্রান্সফার বানিও না। ব্যাকআপ রিস্টোরের পরে অন-চেইন ফল আর লেজার না মেলা পর্যন্ত পেআউট বন্ধ রাখো।

দুই স্বাধীন সেবাদাতা কনফার্ম হওয়া অন-চেইন রিভার্ট প্রমাণ করলেই রিজিউম ব্যর্থ EVM ট্রান্সফার বদলাতে পারে। অজানা ফলের ফান্ড রিজার্ভ থাকে, আর ব্যর্থ চেষ্টার ফিও মূল বাজেটে গণনা হয়।

গ্রাহকের রিফান্ড দরকার

গ্রাহকের নিয়ন্ত্রণে থাকা রিফান্ড অ্যাড্রেস যাচাই করো। সমর্থিত রিফান্ড পুরো আর একই অ্যাসেটে। সব বিক্রেতাকে দেওয়ার পরে প্রধান ওয়ালেট আলাদা ফান্ড করো; Wholly Crypto তাদের থেকে ফেরত কাটতে পারে না। আংশিক দেওয়া বিক্রেতার ব্যাচে হাতে হিসাব মেলাতে হয়, ধরে নেওয়া আংশিক-রিফান্ড বোতাম নয়।

8. খোলার আগে পরীক্ষা করো

  • ছোট BTC এবং/অথবা EVM পেমেন্ট থেকে কনফার্ম হওয়া বিক্রেতার পেআউট পর্যন্ত পরীক্ষা করো। চেইন, টোকেন কনট্রাক্ট, গন্তব্য, কমিশন আর আলাদা ফি নিশ্চিত করো।
  • পুনরাবৃত্ত কলব্যাক, টাইমআউট API রেসপন্স, কম পেমেন্ট আর আটকে থাকা পেআউট পরীক্ষা করো। কোনোটিতে যেন ডুপ্লিকেট অর্ডার পূরণ বা ডুপ্লিকেট পাঠানো না হয়।
  • ব্যক্তিগত ব্যাকআপ সার্ভারের বাইরে রাখো, পেআউট ব্যর্থতা নজরে রাখো আর নিয়মিত বিক্রেতার পাওনা অন-চেইন ফান্ডের সঙ্গে মেলাও।

পর্যালোচিত পেআউট আর অল্প বিক্রেতা দিয়ে শুরু করো। শুধু পরীক্ষিত অ্যাসেট, বাজেট আর গন্তব্য স্বয়ংক্রিয় করো।