Skip to main content

Overview​

When a card transaction occurs, the OPCard platform asynchronously delivers transaction event notifications to the merchant's configured callback URL via Webhook. The merchant's server is responsible for receiving and processing these notifications to keep transaction statuses synchronized, perform reconciliation, and enable risk monitoring.

Typical Use Cases:

  • Keep your internal system synchronized with card transaction statuses in real time.
  • Receive notifications for key events, including refunds, chargebacks, and card loads.
  • Maintain a unified record of fund movements for reconciliation and financial tracking.

Notification Mechanism​

Notification Flow​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Transaction Completed β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” HTTP POST β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ OPCard β”‚ ─────────────────────→ β”‚ Webhook β”‚ ──────────→ β”‚ Merchant β”‚
β”‚ β”‚ β”‚ Notification β”‚ + Signature β”‚ Webhook Endpointβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Service β”‚ Headers β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ Signature Verification
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Return HTTP 200 OK β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Specifications​

ItemDescription
Delivery MethodHTTP POST (application/json)
Webhook EndpointConfigure Webhook URL
Retry PolicyUp to 12 retry attempts. The retry intervals are: 1m, 2m, 4m, 8m, 15m, 30m, 1h, 2h, 4h, 8h, 16h, and 32h.
Delivery TriggerNotifications are sent in real time whenever the transaction status changes.
Success ResponseThe merchant's server must return HTTP 200. Any other response is considered a delivery failure.
IdempotencyEach notify_event_id is globally unique. Merchants should store and deduplicate this value to ensure idempotent processing.
Character EncodingUTF-8
Time ZoneUTC (convert to your local time zone if required).

Event Flow Description​

This section describes the complete lifecycle of subscription events, from event generation to webhook delivery and merchant processing.

Notification Parameters​

ParameterTypeLengthRequiredDescriptionExample
event_code
string-RequiredIdentifies the specific type of event.
  • 165801: Transaction success
  • 165802: Card load event
  • 165803: Transaction failure
  • 165804: Authorization success
  • 165805: Authorization failure
165801
event_type
string-RequiredEvent type
  • card.transaction.success: Transaction success
  • card.load: Card load event
  • card.transaction.failure: Transaction failure
  • card.authorization.success: Authorization success
  • card.authorization.failure: Authorization failure
card.transaction.success
notify_event_id
long-RequiredA unique notification event ID used for idempotent processing, log tracing, and troubleshooting.2062493598283964416
+event_payload
object-RequiredAn object containing the business data associated with the event, including detailed card transaction information.

Signature Verification​

Signature Algorithm and Request Headers​

ItemValue
Signature AlgorithmHMAC-SHA256 (default) / SHA256withRSA
Signature Generation RuleHMAC(secret_key, timestamp + "\n" + payload)
HTTP Request HeadersX-ODP-Timestamp, X-ODP-Signature

Signature Verification Steps (Required on Merchant Server)​

# Python
import hmac
import hashlib

def verify_webhook(headers: dict, body: bytes, secret_key: str) -> bool:
timestamp = headers.get("X-ODP-Timestamp", "")
signature = headers.get("X-ODP-Signature", "")

# 1. Concatenate the signature string (the separator must be `"\n"`, not an empty string).
message = timestamp + "\n" + body.decode("utf-8")

# 2. HMAC-SHA256
expected = hmac.new(
secret_key.encode("utf-8"),
message.encode("utf-8"),
hashlib.sha256
).hexdigest()

# 3. Verify the signature
return hmac.compare_digest(expected, signature)

Signature Verification Notes​

RiskMitigation
Incorrect separator used in signature payloadThe separator must be "\n" (newline character). Do not use an empty string or space.
Secret key leakageThe secret_key must never be hardcoded in the frontend or client-side applications.
Replay attacksValidate the time difference between X-ODP-Timestamp and the server time (recommended ≀ 5 minutes).
Request body modificationSignature verification must be performed against the original request body. Do not parse or modify the body before verification.

Webhook Payload Example​

X-ODP-Timestamp: 1752019200
X-ODP-Signature: 4f7e3d2c1b0a9f8e7d6c5b4a3f2e1d0c...

Body​

{
"event_code": "165801",
"event_type": "card.transaction.success",
"notify_event_id": 2076942421039628288,
"event_payload": {
"billing_amount": "HKD 0.09",
"creation_time": "2026-04-03 15:04:25",
"transaction_id": "1112002320282",
"account_id": "1016X10007748930412",
"program_value": 1,
"transaction_amount": "HKD 0.09",
"transaction_source": "Online",
"merchant_name": "401 Congress",
"transaction_type": "Purchase",
"card_number": "545502 **** 1159",
"originalTransactionId": "1112002320265",
"card_id": "1"
}
}

Frequently Asked Questions (FAQ)​

Q1: Is HTTPS required for the webhook URL?
A: Yes. HTTPS is required for the production environment, and the SSL/TLS certificate must be valid.

Q2: What happens if the merchant does not return HTTP 200?
A: The notification will be added to the retry queue. Merchants are advised to return a response within 4 seconds and process business logic asynchronously.

Q3: Can webhook notifications arrive out of order?
A: Yes, it is possible. Merchants should use transaction_id for transaction correlation and should not rely on the notification order.

Q4: Can the same transaction be notified multiple times?
A: Yes. Merchants should use notify_event_id for idempotency and process each event ID only once.

Q5: Is there a limit on notification frequency?
A: There is currently no rate limit. However, merchants should ensure that the response is returned within 3 seconds.

Q6: Is transaction_amount always formatted with two decimal places?
A: Yes. Merchants should use BigDecimal for parsing to avoid floating-point precision issues.

Q7: Is the full card number transmitted?
A: No. card_no is not the actual card number. It is an internal card identifier or card reference used to identify the card.