Pay Bills
Purchase data, airtime, and digital products for Nigerian phone numbers instantly using wallet balance with network and plan validation.
技能说明
SKILL.md — CreditWithBleon API
Buy data, airtime, and digital products for Nigerian phone numbers via wallet balance.
Base URL: https://lodu.bleon.net/v1
Helper Scripts
These Node.js scripts live in the pay-bills-skill/ directory. Run them with node to generate IDs and manage auth state.
| Script | Command | Purpose |
|---|---|---|
generate-order-id.js | node pay-bills-skill/generate-order-id.js | Outputs a unique ORDER_<timestamp>_<random> string. Use this as trx_id for every order — never hardcode or reuse a trx_id. |
generate-device-id.js | node pay-bills-skill/generate-device-id.js [userId] | Outputs a device ID. With userId: openclaw_<userId>. Use as deviceId in auth requests. |
session-token.js | see below | Manages the session token for auth. |
Session Token Commands
node pay-bills-skill/session-token.js check → { "loggedIn": true/false, "sessionToken": "..." | null }
node pay-bills-skill/session-token.js save <token> → saves the token to .session_token
node pay-bills-skill/session-token.js load → prints the saved token (exit 1 if none)
node pay-bills-skill/session-token.js clear → deletes the saved token (logout)
Workflow
- Before any
[auth]request: runnode pay-bills-skill/session-token.js checkto see if a session token exists.- If
loggedIn: true→ use thesessionTokenvalue asAuthorization: Bearer <sessionToken>. - If
loggedIn: false→ start the login flow (see Auth below).
- If
- After a successful login (steps that return
sessionToken): runnode pay-bills-skill/session-token.js save <sessionToken>to persist it. - Before placing an order: run
node pay-bills-skill/generate-order-id.jsto get a freshtrx_id. - For auth requests: run
node pay-bills-skill/generate-device-id.js(no userId before login). After login, runnode pay-bills-skill/generate-device-id.js <userId>to bind the device to the user. - On logout or 401: run
node pay-bills-skill/session-token.js clearand re-auth.
Auth
All [auth] endpoints need Authorization: Bearer <sessionToken>. If 401 → run node pay-bills-skill/session-token.js clear and re-auth.
Login flow (no auth needed):
1. POST /auth/start { "phoneNumber":"08031234567", "deviceId":"<run: node pay-bills-skill/generate-device-id.js>" }
→ { success, data: { sessionId, nextStep } }
nextStep will be one of:
- "verify_otp" → new user OR unrecognized device
- "enter_pin" → known device, go straight to step 3
2. POST /auth/verify-otp { "sessionId":"<from-step-1>", "code":"123456" } ← 6 digits from SMS
→ { success, data: { sessionId, nextStep } }
nextStep will be one of:
- "set_pin" → new user, needs to create 4-digit PIN
- "enter_pin" → existing user, new device
- "complete_profile" → profile incomplete
2b. POST /auth/set-pin { "sessionId":"...", "pin":"1234" } ← 4 digits (new users only)
→ { success, data: { sessionId, nextStep:"complete_profile" } }
2c. POST /auth/complete-profile { "sessionId":"...", "fullName":"John Doe", "email":"j@x.com" }
→ { success, data: { sessionId, nextStep:"verify_email" } }
2d. POST /auth/verify-email { "sessionId":"...", "code":"123456" } ← 6 digits from email
→ { success, sessionToken, userId } ✓ DONE
→ then run: node pay-bills-skill/session-token.js save <sessionToken>
3. POST /auth/verify-pin { "sessionId":"<from-above>", "pin":"1234" } ← 4 digits
→ { success, sessionToken, userId } ✓ DONE
→ then run: node pay-bills-skill/session-token.js save <sessionToken>
Returning user (known device): steps 1 → 3 only. Returning user (new device): steps 1 → 2 → 3. New user: steps 1 → 2 → 2b → 2c → 2d.
Other auth endpoints:
POST /auth/resend-otp { sessionId }— 60s cooldown, max 5/hourPOST /auth/forgot-pin { phoneNumber, deviceId }→ OTP flow → set new PINPOST /auth/logout { sessionToken }→ then runnode pay-bills-skill/session-token.js clear
Rate limits: OTP: 5 sends/hour, 3 verify attempts/code. PIN: 5 attempts/15min, 5min lockout after.
Buy Data
1. POST /utils/phone/normalize { "phone": "<raw>" } → { ok, data: "08031234567" }
2. POST /utils/phone/predict { "phone": "08031234567" } → { ok, data: { phone, network } }
3. GET /product/networks → [{ id, name, status }]
4. GET /product/networks/:networkId/data-plans → { categories, plans: { daily[], weekly[], monthly[] } }
Each plan: { id, name, amount, durationDays, category, status } — only use status:"active"
5. GET /user/balance [auth] → { ok, balance, points }
6. POST /orders [auth]
{ "type":"data", "payment_method":"wallet", "trx_id":"<run: node pay-bills-skill/generate-order-id.js>", "use_points":false,
"data": { "phone":"08031234567", "data_id": 42 } }
→ { ok, message, transactionId }
7. GET /orders/:transactionId/status [auth] → { status, reference, createdAt }
Buy Airtime
Same steps 1-3, skip step 4. Minimum ₦50.
POST /orders [auth]
{ "type":"airtime", "payment_method":"wallet", "trx_id":"<run: node pay-bills-skill/generate-order-id.js>", "use_points":false,
"data": { "phone":"08031234567", "network_id":1, "amount":500 } }
Order Statuses
| Status | Meaning |
|---|---|
pending_payment | Awaiting online payment |
order_received | Processing — poll every 15-30s |
completed | Delivered |
failed | Failed, user auto-refunded |
Call only if the user wants to know the status of an order they placed.
Other Endpoints (all [auth])
GET /user/balance → { balance, points }
GET /user/profile → { id, balance, fullName, email, phone }
GET /user/deposit-account → { account: { AccountNumber, AccountName, BankName } | null }
GET /transactions?page=&limit=&type=&status=&fromDate=&toDate= → { transactions[], total, page, totalPages }
GET /transactions/recent-phones → { recentlyUsedPhones: [{ phone_number, network_id, network_name }] }
POST /payment/deposit [auth] { amount (min 100) } → { paymentLink }
Notification Preferences (all [auth])
Users can view and update which notifications they receive.
GET /user/notification-preferences → { ok, preferences }
PATCH /user/notification-preferences → { ok, preferences }
Preference Fields (all boolean, all optional on update)
| Field | Description | Default |
|---|---|---|
emailEnabled | Master switch for email channel | true |
chatEnabled | Master switch for chat channel | true |
orderConfirmations | Order placed / confirmed | true |
orderStatusUpdates | Order completed / failed | true |
depositConfirmations | Deposit successful | true |
paymentFailures | Payment failed / underpaid | true |
refundNotifications | Refund processed | true |
giveawayUpdates | Giveaway created / activated | true |
giveawayClaimAlerts | Someone claimed your giveaway | true |
pointsAndCashback | Cashback & points earned | true |
promotionalEmails | Marketing / promotional content | true |
lowBalanceWarning | Balance below threshold | true |
Example
PATCH /user/notification-preferences
{ "promotionalEmails": false, "lowBalanceWarning": false }
→ { "ok": true, "preferences": { ...all fields... } }
Saved Contacts (all [auth])
Users can save phone numbers with names for quick reference. When a user says "buy data for Mum" or "send airtime to John", search their saved contacts first.
GET /contacts?page=&limit=&search= → { contacts[], total, page, totalPages }
POST /contacts { "name":"Mum", "phoneNumber":"09012345678" } → { ok, contact }
PATCH /contacts/:id { "name":"Mother" } or { "phoneNumber":"..." } or both → { ok, contact }
DELETE /contacts/:id → { ok, message:"Contact deleted" }
GET /contacts/search?name=mum → { ok, contacts: [{ id, name, phoneNumber }] } (max 5 results)
- A phone number can only be saved once per user (409 Conflict on duplicate)
searchparam onGET /contactsmatches name or phone (partial)GET /contacts/search?name=is a quick lookup — use it to resolve a name to a phone number
Contact Behaviors
- After a successful purchase: if the phone number isn't already saved, suggest saving it: "Would you like to save 0803 123 4567 with a name so you can quickly buy for them next time?"
- When user refers to a name: search contacts via
GET /contacts/search?name=...- 1 match → use that phone number (confirm with user)
- Multiple matches → show options, ask which one
- No match → ask user for the phone number
- "Buy for Mum" flow: search contacts → find phone → normalize → predict network → fetch plans → confirm → order
Product Discovery (no auth)
GET /product/networks
GET /product/networks/:id/data-plans
GET /product/data-plans/:dataId
GET /product/education
Key Rules
- Always normalize phone before anything —
POST /utils/phone/normalize - Always predict network —
POST /utils/phone/predict(don't guess from prefix) - Never hardcode network IDs or plan IDs — fetch fresh every time
- trx_id must be unique per attempt — run
node pay-bills-skill/generate-order-id.jsfor each order, never reuse even on failure - Check balance before wallet orders — if insufficient, suggest deposit
- Confirm with user before submitting — show plan, price, phone, balance
- Network status must be
up— don't order ondownnetworks - Plan status must be
active— skip disabled plans - payment_method
walletis preferred for agent purchases (instant, no redirect) - Use
GET /transactions/recent-phonesfor quick re-orders ("buy same as last time") - Use
GET /contacts/search?name=when user refers to someone by name ("buy for Mum") - After a successful purchase, suggest saving the number if it's not already in contacts
- User's own phone is in
GET /user/profile→phonefield
Enums
- type:
data,airtime - payment_method:
wallet,online - transaction type:
deposit,purchase_data,purchase_airtime,giveaway_funding,giveaway_claim - plan category:
daily,weekly,monthly,yearly
如何使用「Pay Bills」?
- 打开小龙虾AI(Web 或 iOS App)
- 点击上方「立即使用」按钮,或在对话框中输入任务描述
- 小龙虾AI 会自动匹配并调用「Pay Bills」技能完成任务
- 结果即时呈现,支持继续对话优化