Integrate MEID Payment Provider
This document describes how to add MEID as a new payment provider, similar to linkmepay, btcpay, and zeroxprocessing.
| Field | Value |
|---|---|
| Provider | meid |
| Operation | Deposit |
| Initial scope | Player deposit only |
| Currency | USD unless MEID requires another currency |
| Flow type | Provider-hosted payment URL / payment details |
| Settlement | MEID webhook / callback settles the existing PaymentTransaction |
CDN credit purchase for Shop, Agent, and Super Agent is intentionally out of scope for the first MEID integration.
Current Payment Architectureβ
Deposit creation uses a provider adapter registry:
POST /api/payment/deposit
β
βΌ
payment route validation
β
βΌ
depositService.processDeposit()
β
βΌ
getDepositAdapter(provider)
β
βββ linkmepay
βββ btcpay
βββ zeroxprocessing
β
βΌ
adapter.createPayment()
β
βΌ
PaymentTransaction updated with paymentUrl / provider response
β
βΌ
Gateway callback settles the transaction
MEID should be added as a fourth deposit provider:
getDepositAdapter(provider)
β
βββ linkmepay
βββ btcpay
βββ zeroxprocessing
βββ meid
Relevant files:
| Area | File |
|---|---|
| Deposit validation | kioskgaming_backend/src/routes/payment/validations.js |
| Deposit service | kioskgaming_backend/src/services/payment/depositService.js |
| Multi-game deposit service | kioskgaming_backend/src/services/payment/multiGameDepositService.js |
| Deposit adapter registry | kioskgaming_backend/src/services/payment/depositAdapters/index.js |
| Existing LinkMePay adapter example | kioskgaming_backend/src/services/payment/depositAdapters/linkMePayDepositAdapter.js |
| Existing BTCPay adapter example | kioskgaming_backend/src/services/payment/depositAdapters/btcPayDepositAdapter.js |
| Existing 0x adapter example | kioskgaming_backend/src/services/payment/depositAdapters/zeroxProcessingDepositAdapter.js |
| Public payment routes | kioskgaming_backend/src/routes/payment.js |
| User payment controller callbacks | kioskgaming_backend/src/controllers/user/paymentUserController.js |
| Provider fee config | kioskgaming_backend/src/services/core/paymentProviderFeeCoreService.js |
| Payment method catalog | kioskgaming_backend/src/services/biz/paymentMethodCatalogBizService.js |
Required Backend Changesβ
1. Add meid To Provider Validationβ
Update:
kioskgaming_backend/src/routes/payment/validations.js
Add meid anywhere provider validation currently allows:
['linkmepay', 'btcpay', 'zeroxprocessing', 'meid']
This applies to:
| Validation | Needed for |
|---|---|
depositValidation | Player wallet-only and direct-to-game deposits |
multiGameDepositValidation | Player multi-game deposits |
withdrawalValidation | Only if MEID supports withdrawals |
If MEID is deposit-only, do not add it to withdrawal validation yet.
2. Add meid To Deposit Service Provider Guardβ
Update:
kioskgaming_backend/src/services/payment/depositService.js
Current guard:
if (provider !== 'linkmepay' && provider !== 'zeroxprocessing' && provider !== 'btcpay') {
throw new Error('Invalid provider');
}
Expected:
if (
provider !== 'linkmepay' &&
provider !== 'zeroxprocessing' &&
provider !== 'btcpay' &&
provider !== 'meid'
) {
throw new Error('Invalid provider');
}
3. Create MEID Deposit Adapterβ
Add:
kioskgaming_backend/src/services/payment/depositAdapters/meidDepositAdapter.js
The adapter must expose:
async function createPayment(params) {
// params: { transaction, user, amount, currency, paymentMethod, description }
}
module.exports = {
createPayment
};
The normalized adapter response must match the existing adapter contract:
{
success: true,
transactionId: 'MEID_PROVIDER_TRANSACTION_ID',
paymentUrl: 'https://meid.example/checkout/...',
invoiceId: 'MEID_INVOICE_ID',
paymentAddress: null,
endTime: null,
expiryTime: null,
requestData: {},
data: {}
}
Required contract:
| Field | Requirement |
|---|---|
success | true only when MEID order creation succeeds |
transactionId | Provider-facing transaction ID or current internal transaction ID |
paymentUrl | Required when success = true, unless MEID returns non-URL payment details and frontend supports that |
invoiceId | MEID order / invoice ID if available |
requestData | Exact outbound provider request, for admin/debug |
data | Raw provider response |
4. Register MEID Adapterβ
Update:
kioskgaming_backend/src/services/payment/depositAdapters/index.js
Add:
const meidDepositAdapter = require('./meidDepositAdapter');
Add it to ADAPTERS:
const ADAPTERS = {
btcpay: btcPayDepositAdapter,
linkmepay: linkMePayDepositAdapter,
zeroxprocessing: zeroxProcessingDepositAdapter,
meid: meidDepositAdapter
};
Also export it if needed for tests:
meidDepositAdapter
5. Add MEID Provider Serviceβ
Recommended structure:
kioskgaming_backend/src/services/payment/meid/
βββ config.js
βββ helpers.js
βββ meidService.js
Expected responsibilities:
| File | Responsibility |
|---|---|
config.js | Base URL, endpoint paths, timeout, credentials from env |
helpers.js | Signature generation / verification, payload normalization |
meidService.js | Create payment, verify callback, query transaction if supported |
Environment variables should be explicit:
MEID_BASE_URL=
MEID_MERCHANT_ID=
MEID_PUBLIC_KEY=
MEID_PRIVATE_KEY=
MEID_CALLBACK_SECRET=
MEID_SANDBOX=true
Adjust names to match MEID's real API contract.
Callback / Webhook Flowβ
Add MEID public callback routes in:
kioskgaming_backend/src/routes/payment.js
Example:
router.post('/meid/webhook', paymentUserController.handleMEIDWebhook.bind(paymentUserController));
router.post('/meid/ipn/:transaction_id', paymentUserController.handleMEIDCallback.bind(paymentUserController));
Choose the route shape based on MEID's actual webhook contract:
| MEID callback style | Recommended route |
|---|---|
| Provider sends transaction ID in path | /api/payment/meid/ipn/:transaction_id |
| Provider sends transaction ID in body | /api/payment/meid/webhook |
In paymentUserController, add a handler that:
- Verifies MEID signature.
- Normalizes provider payload.
- Finds
PaymentTransactionby internal transaction ID, provider transaction ID, or invoice ID. - Saves callback log / raw provider response.
- Delegates settlement to the existing deposit callback / settlement service.
- Returns the response format MEID expects.
Player Deposit Flowβ
Player selects MEID on deposit screen
β
βΌ
Frontend submits:
POST /api/payment/deposit
{
"amount": 50,
"provider": "meid"
}
β
βΌ
Backend creates PaymentTransaction
provider = 'meid'
paymentMethod = null or MEID sub-method
status = 'pending'
β
βΌ
MEID adapter creates provider payment order
β
βΌ
MEID returns payment URL / payment details
β
βΌ
User completes payment
β
βΌ
MEID callback hits backend
β
βΌ
Existing settlement flow credits player wallet or game wallet
Example request:
POST /api/payment/deposit
Authorization: Bearer USER_TOKEN
Content-Type: application/json
{
"amount": 50,
"provider": "meid",
"description": "Player deposit via MEID"
}
Expected response:
{
"success": true,
"data": {
"transactionId": "MEID_OR_INTERNAL_TRANSACTION_ID",
"paymentUrl": "https://...",
"amount": 50,
"currency": "USD",
"status": "pending",
"syncStatus": "none"
}
}
Supported Methods Endpointβ
Update:
kioskgaming_backend/src/services/biz/paymentMethodCatalogBizService.js
For provider meid, return supported methods or a single default method.
Example:
GET /api/payment/supported-methods?provider=meid&operation=deposit
Expected shape:
{
"methods": [
{
"id": "meid",
"methodId": "meid",
"name": "MEID",
"currency": "USD",
"min": 10,
"max": 1000,
"comingSoon": false,
"sortOrder": 0
}
]
}
If MEID has multiple rails or channels, expose them as sub-methods:
{
"methods": [
{
"id": "meid_default",
"methodId": "meid_default",
"name": "MEID",
"currency": "USD"
}
]
}
Fee Configurationβ
The code resolves deposit fee snapshots through:
resolveProviderFeeRate({
provider,
operation: 'deposit',
methodId: paymentMethod || 'all'
})
Add fee config rows for MEID:
| provider | operation | method_id | fee_rate |
|---|---|---|---|
meid | deposit | all | Product-defined |
meid | deposit | meid | Optional method-specific override |
If MEID supports withdrawal later:
| provider | operation | method_id | fee_rate |
|---|---|---|---|
meid | withdrawal | all | Product-defined |
Transaction Data Requirementsβ
For every MEID deposit transaction:
| Field | Expected value |
|---|---|
type | deposit |
provider | meid |
paymentMethod | meid, MEID sub-method, or null |
currency | USD unless MEID requires another currency |
status at creation | pending |
paymentUrl | Present after MEID order creation if MEID uses hosted checkout |
externalTransactionId | MEID order / invoice ID if available |
providerRequest | Raw outbound MEID create-payment request |
paymentResult | Normalized adapter result plus raw MEID response |
Error Casesβ
| Case | Expected behavior |
|---|---|
provider = 'meid' not added to validation | API returns 422 before reaching service |
provider = 'meid' not added to adapter registry | Service throws unsupported provider |
| MEID create-payment API fails | Transaction is marked failed; request and response are saved |
| MEID returns success without checkout details | Adapter should return failure unless frontend supports the returned payment details |
| MEID callback signature invalid | Reject callback and do not settle transaction |
| MEID callback references unknown transaction | Log callback; return provider-safe error response |
| Duplicate MEID callback | Existing idempotency guards should prevent double credit |
Test Casesβ
| # | Case | Steps | Expected result |
|---|---|---|---|
| 1 | Player MEID deposit passes validation | POST /api/payment/deposit with provider='meid' | Request reaches service; no provider validation error |
| 2 | MEID adapter is selected | Mock meidDepositAdapter.createPayment() | Adapter called once with transaction, user, amount, currency |
| 3 | MEID creates payment successfully | Mock MEID success response | HTTP 200; transaction has provider='meid', status='pending', paymentUrl saved |
| 4 | MEID create-payment failure is persisted | Mock MEID API error | Transaction status becomes failed; providerRequest / paymentResult saved |
| 5 | MEID callback settles wallet-only deposit | Simulate successful MEID webhook | Transaction moves to completed; player wallet credited once |
| 6 | Duplicate MEID callback is idempotent | Send same successful webhook twice | No double wallet credit |
| 7 | Invalid MEID callback signature is rejected | Send webhook with bad signature | Transaction remains unchanged |
| 8 | Supported methods includes MEID | GET /api/payment/supported-methods?provider=meid&operation=deposit | Response includes method meid |
| 9 | Fee snapshot resolves for MEID | Create MEID deposit with fee config | platformFeeRate and platformFeeAmount saved on transaction |
Implementation Checklistβ
- Confirm MEID API documentation: create payment endpoint, callback format, signature rules, status values, and required response body.
- Add MEID env vars and config.
- Implement
meidService. - Implement
meidDepositAdapter. - Register
meidin deposit adapter registry. - Add
meidto deposit provider validation. - Add
meidtodepositServiceprovider guard. - Add MEID callback route and controller handler.
- Normalize MEID callback statuses to existing transaction statuses.
- Add MEID to supported-methods response.
- Add MEID provider fee config rows.
- Add integration tests for player deposit, callback settlement, and duplicate callback.
- Confirm frontend sends
provider = 'meid'.
Open Questionsβ
| Question | Owner |
|---|---|
| What are MEID's create-payment endpoint, request fields, and success response shape? | Payment / MEID |
Does MEID return a hosted paymentUrl, QR/payment address, or both? | Payment / MEID |
| What signature algorithm does MEID use for requests and callbacks? | Payment / MEID |
What callback statuses map to pending, processing, completed, failed, cancelled, and expired? | Payment / MEID |
| Does MEID support only deposits, or deposits and withdrawals? | Product / Payment |
| What are MEID min/max amount limits and fee rates? | Product / Payment |