Skip to content

Query vs Execute

Understanding the difference between Query and Execute is one of the most important concepts in building a secure, reliable Bohudur integration. Using the wrong one can cause duplicate orders, repeated product delivery, or payment fraud.

At a Glance

Query

Read Only

Checks the current status of a payment. Can be called unlimited times. Never changes anything. Safe to use anywhere.

Execute

Write Once

Finalizes a payment permanently. Can only be called exactly once. Triggers your order fulfillment. Blocks all repeat attempts.

QueryExecute
EndpointPOST /query/v2/POST /execute/v2/
PurposeCheck payment statusFinalize and process payment
Can be calledUnlimited timesExactly once
Changes payment stateNoYes — COMPLETED → EXECUTED
Triggers fulfillmentNeverYes
Safe on page reloadYesNo — causes error 3108
Duplicate protectionNoneBuilt-in — blocks repeats
Error on repeat callNever3108 — Payment already executed
Use forStatus display, admin panels, logs, webhooksOrder processing, product delivery, subscriptions

Complete Payment Flow

This is the full payment lifecycle showing where Query and Execute fit:

1. Your server calls POST /create/v2/ → gets payment_url
2. Redirect customer to payment_url (Bohudur hosted checkout)
Customer pays
Status → COMPLETED
Customer cancels
Status → CANCELLED
↓ (on success)
3. Customer redirected to your redirect_url?paymentkey=XXX
WRONG
Call Query → check COMPLETED → fulfill order
Repeatable! Exploitable!
CORRECT
Call Execute → status becomes EXECUTED → fulfill order
One-time. Secure.
4. Webhook notification sent to your server (if configured)
5. Use Query anytime after to check status, display receipts, audit logs

What is Query?

POST /query/v2/ is a read-only operation. It retrieves the current state of a payment without changing anything.

bash
curl -X POST "https://request.bohudur.one/query/v2/" \
  -H "Content-Type: application/json" \
  -H "AH-BOHUDUR-API-KEY: YOUR_API_KEY" \
  -d '{"paymentkey": "YOUR_PAYMENT_KEY"}'

Query returns one of four statuses:

StatusMeaning
PENDINGCustomer hasn't paid yet
COMPLETEDCustomer paid — ready for Execute
EXECUTEDPayment finalized by your server
CANCELLEDPayment was cancelled

Safe uses of Query:

  • Show order status to a customer
  • Admin panel transaction lookup
  • Cron job polling for pending payments
  • Logging and auditing
  • Verifying a webhook before acting on it

Never use Query to trigger fulfillment — because it can be called repeatedly.

What is Execute?

POST /execute/v2/ is a write-once operation. It permanently changes the payment status from COMPLETED to EXECUTED and returns the full payment data.

bash
curl -X POST "https://request.bohudur.one/execute/v2/" \
  -H "Content-Type: application/json" \
  -H "AH-BOHUDUR-API-KEY: YOUR_API_KEY" \
  -d '{"paymentkey": "YOUR_PAYMENT_KEY"}'

First call — succeeds:

json
{
  "full_name": "Jane Doe",
  "email": "[email protected]",
  "amount": 150,
  "paymentkey": "5RWS4w2w1R5nFAvoP5U0JS4O74UrMXGt",
  "receipt": "https://pay.bohudur.one/receipt/download/102f89389f9e",
  "status": "EXECUTED"
}

Any repeat call — permanently blocked:

json
{
  "responseCode": 3108,
  "message": "Payment already executed!",
  "status": "failed"
}

The Duplicate Order Problem — Visualized

After payment, your app redirects the customer to:

https://example.com/order/success/?paymentkey=5RWS4w2w1R5nFAvoP5U0JS4O74UrMXGt

This URL is visible in the browser. The customer can copy it, bookmark it, share it, or revisit it anytime.

❌ What happens if you use Query to fulfill orders:

Day 1

Customer visits success URL → Query returns COMPLETEDOrder #1001 processed Order sent ❌

Day 2

Customer revisits the same URL → Query returns EXECUTEDOrder #1001 processed again Duplicate ❌

Day 7

Customer shares URL with a friend → Friend visits → Order #1001 processed a 3rd time Fraud ❌

Day 30

Anyone with the URL → Query still returns data → Order processed forever No protection ❌

This is a real vulnerability

Any person with the success URL can trigger order fulfillment unlimited times. Query has no protection against this — it is not designed to be a fulfillment trigger.

What happens if you use Execute to fulfill orders:

Day 1

Customer visits success URL → Execute called → Order #1001 processed → status becomes EXECUTED Order sent

Day 2

Customer revisits the same URL → Execute returns 3108Order blocked Protected

Day 7

Customer shares URL with friend → Execute returns 3108Blocked Protected

Day 30

Anyone with the URL → Execute returns 3108Always blocked forever Safe

Execute is your built-in protection

The first Execute wins. Every attempt after that is permanently rejected — no matter who makes the request or when. One payment. One fulfillment. Always.

State Machine — Payment Lifecycle

                    ┌─────────────┐
                    │   CREATED   │
                    └──────┬──────┘
                           │ Customer redirected to checkout

                    ┌─────────────┐
                    │   PENDING   │ ◄── Query: returns PENDING
                    └──────┬──────┘

              ┌────────────┴────────────┐
              │ Customer pays           │ Customer cancels
              ▼                         ▼
       ┌─────────────┐          ┌─────────────┐
       │  COMPLETED  │          │  CANCELLED  │ ◄── Query: returns CANCELLED
       └──────┬──────┘          └─────────────┘    Execute: error 3107

              │ Query: returns COMPLETED (read-only, no side effects)
              │ Execute: finalizes payment (one-time only)


       ┌─────────────┐
       │   EXECUTED  │ ◄── Query: returns EXECUTED
       └─────────────┘     Execute: error 3108 (blocked forever)

The Correct Code Pattern

php
// success.php — called when customer returns from checkout
$paymentkey = $_GET['paymentkey'];

// Use Execute — finalizes payment, one-time only
$execute = Bohudur::Execute($paymentkey);

if ($execute->status === 'EXECUTED') {
    // Payment finalized — fulfill the order exactly once
    $orderId = $execute->metadata->order_id;
    fulfillOrder($orderId);
    saveToDatabase($execute);
    sendConfirmationEmail($execute->email, $execute->receipt);
    showSuccessPage();
} else {
    // Payment pending, cancelled, or already executed
    showErrorPage($execute->status);
}
php
// success.php — WRONG APPROACH
$paymentkey = $_GET['paymentkey'];

// Query does not protect against repeated calls
$query = Bohudur::Query($paymentkey);

// This runs EVERY TIME the page loads — duplicates guaranteed
if ($query->status === 'COMPLETED' || $query->status === 'EXECUTED') {
    fulfillOrder($query->metadata->order_id); // ❌ Runs on every visit!
}

When to Use Each — Decision Guide

I need to...                                 Use
─────────────────────────────────────────────────────
Fulfill an order after payment          →   Execute
Deliver a digital product               →   Execute
Activate a subscription                 →   Execute
Confirm a booking                       →   Execute

Check if customer has paid              →   Query
Show payment status on a page           →   Query
Poll from a background job              →   Query
Admin panel transaction lookup          →   Query
Verify a webhook payload                →   Query
Audit/log a transaction                 →   Query

Execute Error Codes

CodeMessageMeaning
3100API key not foundAH-BOHUDUR-API-KEY header is missing
3101API key not validAPI key is invalid or inactive
3102Invalid Payment Keypaymentkey is malformed or doesn't exist
3104You don't have accessYour server IP is not authorized
3105Payment Data Not FoundNo payment found for this key
3106Payment is pending!Customer hasn't paid yet — wait or poll
3107Payment is cancelled!Cannot execute a cancelled payment
3108Payment already executed!Already finalized — this IS the protection
3109Failed to execute paymentServer error — retry once

Handle 3108 gracefully

3108 is not always an error in your logic — it means the payment was already finalized. Show a "your order is already confirmed" message rather than an error screen.


বাংলা ব্যাখ্যা — Query বনাম Execute

Bohudur payment integration-এ Query এবং Execute — এই দুটি API call-এর পার্থক্য বোঝা অত্যন্ত জরুরি। ভুল ব্যবহার করলে একই অর্ডার বারবার process হতে পারে।


Query কী?

Query

Query হলো শুধুমাত্র পড়ার জন্য (Read Only)। এটি payment-এর বর্তমান অবস্থা দেখায়, কিন্তু কোনো পরিবর্তন করে না। যতবার ইচ্ছা ডাকা যায়।

Query কখন ব্যবহার করবেন:

  • Customer-কে payment status দেখাতে
  • Admin panel-এ transaction দেখতে
  • Background job থেকে payment check করতে
  • Webhook verify করতে

Execute কী?

Execute

Execute হলো payment চূড়ান্ত করার জন্য (Write Once)। এটি payment-এর status COMPLETED থেকে EXECUTED-এ বদলে দেয়। এটি সারাজীবনে একটি payment-এর জন্য মাত্র একবারই সফল হবে।

Execute কখন ব্যবহার করবেন:

  • Order process করতে
  • Digital product deliver করতে
  • Subscription activate করতে
  • যেকোনো কাজ যা একবারই হওয়া উচিত

সমস্যাটা কোথায়?

Customer payment করার পর আপনার success page-এ redirect হয়:

https://example.com/order/success/?paymentkey=5RWS4w2w...

এই URL টি browser-এ দেখা যায়। Customer এটি copy করে রাখতে পারে, bookmark করতে পারে, বা অন্যকে share করতে পারে।

ভুল পদ্ধতি (Query দিয়ে order process করা):
যদি আপনি success page-এ Query করে order process করেন — তাহলে যতবার কেউ সেই URL visit করবে, ততবার order process হবে। ১ম দিন customer visit করলো → order গেলো। ২য় দিন আবার visit করলো → আবার order গেলো। ৭ম দিন অন্য কেউ URL পেয়ে visit করলো → আবার order গেলো। কোনো সুরক্ষা নেই।

সঠিক পদ্ধতি (Execute দিয়ে order process করা):
যদি আপনি Execute ব্যবহার করেন — ১ম বার সফল হবে এবং order যাবে। এরপর যতবারই কেউ সেই URL visit করুক না কেন, Execute error 3108 দেবে এবং order আর process হবে না। একটি payment, একটি order — সবসময়।


সহজ মনে রাখার উপায়

Query = একটি বইয়ের মতো। যতবার খুশি পড়া যায়, কিন্তু বই পড়লে বইয়ের ভেতরে কিছু বদলায় না।

Execute = একটি চেকের মতো। একবার ব্যাংকে জমা দিলে আর দ্বিতীয়বার জমা দেওয়া যায় না — rejected হবে।


সংক্ষেপে

QueryExecute
কতবার ডাকা যায়অসীমবারমাত্র একবার
কী করেশুধু দেখায়চূড়ান্ত করে
Order process করতেকখনো নাহ্যাঁ
সুরক্ষা আছে?নেইআছে

Summary

  • Query → Read-only. Unlimited calls. Never changes anything. Use for display and checks.
  • Execute → Write-once. One call per payment. Finalizes and protects. Use for fulfillment.
  • Rule: Always Execute first on your success page. Use Query for everything after.

Support

Bohudur is free and open to everyone. No trade license required.