Back to skills
SkillHub ClubShip Full StackFull Stack

mayar-payment

Imported from https://github.com/openclaw/skills.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,070
Hot score
99
Updated
March 19, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
F17.6

Install command

npx @skill-hub/cli install openclaw-skills-mayar-payment

Repository

openclaw/skills

Skill path: skills/ahsanatha/mayar-payment

Imported from https://github.com/openclaw/skills.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install mayar-payment into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding mayar-payment to shared team environments
  • Use mayar-payment for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: mayar-payment
description: Mayar.id payment integration for generating invoices, payment links, and tracking transactions via MCP. Use when needing to: (1) Create payment invoices/links for customers, (2) Track payment status and transactions, (3) Generate WhatsApp-friendly payment messages, (4) Handle Indonesian payment methods (bank transfer, e-wallet, QRIS), (5) Manage subscriptions/memberships, or (6) Automate payment workflows for e-commerce, services, or digital products.
---

# Mayar Payment Integration

Integrate Mayar.id payment platform via MCP (Model Context Protocol) for Indonesian payment processing.

## Prerequisites

1. **Mayar.id account** - Sign up at https://mayar.id
2. **API Key** - Generate from https://web.mayar.id/api-keys
3. **mcporter configured** - MCP must be set up in Clawdbot

## Setup

### 1. Store API Credentials

```bash
mkdir -p ~/.config/mayar
cat > ~/.config/mayar/credentials << EOF
MAYAR_API_TOKEN="your-jwt-token-here"
EOF
chmod 600 ~/.config/mayar/credentials
```

### 2. Configure MCP Server

Add to `config/mcporter.json`:

```json
{
  "mcpServers": {
    "mayar": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.mayar.id/sse",
        "--header",
        "Authorization:YOUR_API_TOKEN_HERE"
      ]
    }
  }
}
```

Replace `YOUR_API_TOKEN_HERE` with actual token.

### 3. Test Connection

```bash
mcporter list mayar
```

Should show 15+ available tools.

## Core Workflows

### Create Invoice with Payment Link

**Most common use case:** Generate payment link for customer.

```bash
mcporter call mayar.create_invoice \
  name="Customer Name" \
  email="[email protected]" \
  mobile="\"628xxx\"" \
  description="Order description" \
  redirectURL="https://yoursite.com/thanks" \
  expiredAt="2026-12-31T23:59:59+07:00" \
  items='[{"quantity":1,"rate":500000,"description":"Product A"}]'
```

**Returns:**
```json
{
  "id": "uuid",
  "transactionId": "uuid", 
  "link": "https://subdomain.myr.id/invoices/slug",
  "expiredAt": 1234567890
}
```

**Key fields:**
- `mobile` - MUST be string with quotes: `"\"628xxx\""`
- `expiredAt` - ISO 8601 format with timezone
- `items` - Array of `{quantity, rate, description}`
- `redirectURL` - Where customer goes after payment

### WhatsApp Integration Pattern

```javascript
// 1. Create invoice
const invoice = /* mcporter call mayar.create_invoice */;

// 2. Format message
const message = `
āœ… *Order Confirmed!*

*Items:*
• Product Name
  Rp ${amount.toLocaleString('id-ID')}

*TOTAL: Rp ${total.toLocaleString('id-ID')}*

šŸ’³ *Pembayaran:*
${invoice.data.link}

ā° Berlaku sampai: ${expiryDate}

Terima kasih! šŸ™
`.trim();

// 3. Send via WhatsApp
message({
  action: 'send',
  channel: 'whatsapp',
  target: customerPhone,
  message: message
});
```

### Check Payment Status

```bash
# Get latest transactions (check if paid)
mcporter call mayar.get_latest_transactions page:1 pageSize:10

# Get unpaid invoices
mcporter call mayar.get_latest_unpaid_transactions page:1 pageSize:10
```

Filter by status: `"created"` (unpaid) → `"paid"` (success).

### Other Operations

```bash
# Check account balance
mcporter call mayar.get_balance

# Get customer details
mcporter call mayar.get_customer_detail \
  customerName="Name" \
  customerEmail="[email protected]" \
  page:1 pageSize:10

# Filter by time period
mcporter call mayar.get_transactions_by_time_period \
  page:1 pageSize:10 \
  period:"this_month" \
  sortField:"createdAt" \
  sortOrder:"DESC"
```

## Common Patterns

### Multi-Item Invoice

```javascript
items='[
  {"quantity":2,"rate":500000,"description":"Product A"},
  {"quantity":1,"rate":1000000,"description":"Product B"}
]'
// Total: 2M (2Ɨ500K + 1Ɨ1M)
```

### Subscription/Recurring

Use membership tools:

```bash
mcporter call mayar.get_membership_customer_by_specific_product \
  productName:"Premium Membership" \
  productLink:"your-product-link" \
  productId:"product-uuid" \
  page:1 pageSize:10 \
  memberStatus:"active"
```

### Payment Confirmation Flow

**Option A: Webhook** (Real-time)
- Register webhook URL with Mayar
- Receive instant payment notifications
- Best for production

**Option B: Polling** (Simpler)
- Poll `get_latest_transactions` every 30-60s
- Check for new payments
- Best for MVP/testing

## Troubleshooting

**404 on payment link:**
- Link format: `https://your-subdomain.myr.id/invoices/slug`
- Check dashboard for correct subdomain
- Default may be account name

**Invalid mobile number:**
- Mobile MUST be string: `"\"628xxx\""` (with escaped quotes)
- Format: `628xxxxxxxxxx` (no + or spaces)

**Expired invoice:**
- Default expiry is `expiredAt` timestamp
- Customer can't pay after expiration
- Create new invoice if needed

## Reference Documentation

- **API Details:** See [references/api-reference.md](references/api-reference.md)
- **Integration Examples:** See [references/integration-examples.md](references/integration-examples.md)
- **MCP Tools Reference:** See [references/mcp-tools.md](references/mcp-tools.md)

## Production Checklist

- [ ] Use production API key (not sandbox)
- [ ] Setup webhook for payment notifications
- [ ] Error handling for failed invoice creation
- [ ] Store invoice IDs for tracking
- [ ] Handle payment expiration
- [ ] Customer database integration
- [ ] Receipt/confirmation automation

## Environments

**Production:**
- Dashboard: https://web.mayar.id
- API Base: `https://api.mayar.id/hl/v1/`

**Sandbox (Testing):**
- Dashboard: https://web.mayar.club
- API Base: `https://api.mayar.club/hl/v1/`


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/api-reference.md

```markdown
# Mayar.id API Reference

## Overview

Mayar.id provides a headless e-commerce API for Indonesian payment processing. This reference covers the REST API endpoints (alternative to MCP).

**Official Documentation:** https://docs.mayar.id/api-reference/introduction

## Authentication

All API requests require Bearer token authentication.

```bash
Authorization: Bearer YOUR_JWT_TOKEN
```

Get your API key from:
- **Production:** https://web.mayar.id/api-keys
- **Sandbox:** https://web.mayar.club/api-keys

## Base URLs

**Production:**
```
https://api.mayar.id/hl/v1/
```

**Sandbox (Testing):**
```
https://api.mayar.club/hl/v1/
```

## Core Endpoints

### Invoice

#### Create Invoice (POST)

```
POST /hl/v1/invoice/create
```

**Request Body:**
```json
{
  "name": "Customer Name",
  "email": "[email protected]",
  "mobile": "6281234567890",
  "description": "Order description",
  "redirectURL": "https://yoursite.com/success",
  "expiredAt": "2026-12-31T23:59:59+07:00",
  "items": [
    {
      "quantity": 1,
      "rate": 500000,
      "description": "Product A"
    }
  ]
}
```

**Response:**
```json
{
  "statusCode": 200,
  "messages": "success",
  "data": {
    "id": "uuid",
    "transactionId": "uuid",
    "link": "https://subdomain.myr.id/invoices/slug",
    "expiredAt": 1234567890123
  }
}
```

#### Get Invoice List (GET)

```
GET /hl/v1/invoice
```

**Query Parameters:**
- `page` - Page number
- `pageSize` - Items per page

#### Get Invoice Detail (GET)

```
GET /hl/v1/invoice/:invoiceId
```

#### Edit Invoice (POST)

```
POST /hl/v1/invoice/edit
```

#### Close Invoice (GET)

```
GET /hl/v1/invoice/:invoiceId/close
```

#### Reopen Invoice (GET)

```
GET /hl/v1/invoice/:invoiceId/reopen
```

### Request Payment

#### Create Single Payment Request (POST)

```
POST /hl/v1/request-payment/create
```

**Request Body:**
```json
{
  "name": "Customer Name",
  "email": "[email protected]",
  "mobile": "6281234567890",
  "amount": 500000,
  "description": "Simple payment",
  "redirectURL": "https://yoursite.com/success",
  "expiredAt": "2026-12-31T23:59:59+07:00"
}
```

**Difference from Invoice:**
- `amount` instead of `items[]`
- Simpler for single-amount payments
- No itemization

### Product

#### Get Product Page (GET)

```
GET /hl/v1/product/page
```

#### Search Product (GET)

```
GET /hl/v1/product/search?q=keyword
```

#### Get Product Detail (GET)

```
GET /hl/v1/product/:productId
```

### Customer

#### Get Customer (GET)

```
GET /hl/v1/customer/:customerId
```

#### Create Customer (POST)

```
POST /hl/v1/customer/create
```

**Request Body:**
```json
{
  "name": "Customer Name",
  "email": "[email protected]",
  "mobile": "6281234567890"
}
```

#### Update Customer Email (POST)

```
POST /hl/v1/customer/update-email
```

#### Create Magic Link (POST)

```
POST /hl/v1/customer/magic-link
```

Generates passwordless login link for customer portal.

### Webhook

#### Register URL Hook (POST)

```
POST /hl/v1/webhook/register
```

**Request Body:**
```json
{
  "urlHook": "https://yoursite.com/webhook"
}
```

**Response:**
```json
{
  "statusCode": 200,
  "messages": "success"
}
```

#### Test URL Hook (POST)

```
POST /hl/v1/webhook/test
```

Sends test webhook to registered URL.

#### Get Webhook History (GET)

```
GET /hl/v1/webhook/history
```

#### Retry Webhook (POST)

```
POST /hl/v1/webhook/retry
```

## Webhook Payload

When payment is successful, Mayar sends POST request to your registered webhook URL:

```json
{
  "eventType": "transaction.paid",
  "data": {
    "id": "transaction-uuid",
    "invoiceId": "invoice-uuid",
    "customerId": "customer-uuid",
    "amount": 500000,
    "status": "paid",
    "paymentMethod": "bank_transfer",
    "paidAt": "2026-01-31T12:34:56+07:00",
    "customer": {
      "name": "Customer Name",
      "email": "[email protected]",
      "mobile": "6281234567890"
    }
  }
}
```

**Event Types:**
- `transaction.paid` - Payment successful
- `transaction.failed` - Payment failed
- `transaction.expired` - Invoice expired
- `transaction.refunded` - Payment refunded

## Payment Methods

Mayar supports various Indonesian payment methods:

**Bank Transfer:**
- BCA
- Mandiri
- BNI
- BRI
- Permata
- CIMB Niaga

**E-Wallet:**
- GoPay
- OVO
- DANA
- LinkAja
- ShopeePay

**Others:**
- QRIS (QR Code)
- Virtual Account
- Credit/Debit Card (via partner)

## Data Formats

### Phone Numbers
- Format: `"628xxxxxxxxxx"`
- No + symbol
- No spaces or dashes
- Country code 62 (Indonesia)

### Dates (ISO 8601)
```
2026-12-31T23:59:59+07:00
```
- Include timezone (+07:00 for Jakarta)
- YYYY-MM-DDTHH:mm:ss+TZ

### Currency
- Integer (no decimals)
- In Rupiah
- Example: `500000` = Rp 500,000

### Status Values

**Invoice:**
- `created` - Unpaid
- `paid` - Payment successful
- `expired` - Past expiration date
- `cancelled` - Manually cancelled

**Transaction:**
- `pending` - Awaiting payment
- `processing` - Payment being verified
- `paid` - Payment confirmed
- `failed` - Payment failed
- `refunded` - Payment refunded

**Membership:**
- `active` - Currently active
- `inactive` - Not active
- `finished` - Completed
- `stopped` - Manually stopped

## Rate Limits

Mayar does not publicly document rate limits, but recommended practice:
- Max 10 requests/second per endpoint
- Use exponential backoff for retries
- Cache responses when possible

## Error Codes

```json
{
  "statusCode": 400,
  "messages": "Invalid request",
  "errors": [
    {
      "field": "email",
      "message": "Invalid email format"
    }
  ]
}
```

**Common Status Codes:**
- `200` - Success
- `400` - Bad Request (validation error)
- `401` - Unauthorized (invalid API key)
- `404` - Not Found
- `422` - Unprocessable Entity (business logic error)
- `500` - Internal Server Error

## Pagination

Most list endpoints support pagination:

**Query Parameters:**
- `page` - Page number (starts from 1)
- `pageSize` - Items per page (default: 10, max: 100)

**Response:**
```json
{
  "currentPage": 1,
  "totalPage": 5,
  "pageSize": 10,
  "totalData": 47,
  "nextPage": 2,
  "previousPage": null,
  "data": [...]
}
```

## Filtering & Sorting

**Time-based filtering:**
- `period` - `today`, `this_week`, `this_month`, `this_year`
- `startAt` / `endAt` - Custom date range (ISO 8601)

**Sorting:**
- `sortField` - Field to sort by (`createdAt`, `amount`)
- `sortOrder` - `ASC` or `DESC`

## Best Practices

### 1. Use Idempotency
Store invoice IDs to avoid duplicate invoice creation:

```javascript
const existingInvoice = cache.get(orderId);
if (existingInvoice) {
  return existingInvoice;
}

const newInvoice = await createInvoice(...);
cache.set(orderId, newInvoice);
```

### 2. Handle Webhooks Properly
```javascript
app.post('/webhook', (req, res) => {
  // Respond immediately
  res.status(200).json({ received: true });
  
  // Process async
  processPayment(req.body).catch(console.error);
});
```

### 3. Validate Webhook Signature
(If Mayar provides signature verification - check docs)

### 4. Set Appropriate Expiry
```javascript
// Short for immediate purchase (24h)
const expiry = new Date(Date.now() + 24 * 60 * 60 * 1000);

// Longer for invoices (7-30 days)
const expiry = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
```

### 5. Store Transaction Data
```javascript
{
  orderId: "ORD-123",
  invoiceId: "mayar-invoice-uuid",
  transactionId: "mayar-transaction-uuid",
  customerId: "customer-uuid",
  amount: 500000,
  status: "pending",
  createdAt: "2026-01-31T12:00:00Z",
  paidAt: null
}
```

## Sandbox vs Production

**Sandbox:**
- Use for testing
- No real payments
- Separate dashboard (web.mayar.club)
- Separate API keys

**Production:**
- Real payments
- Live dashboard (web.mayar.id)
- Production API keys
- Use after thorough testing

**Migration:**
- Generate new API key from production
- Update base URL
- Test with small amount first

## Support

- **Community:** https://t.me/mcngroup
- **Blog:** https://blog.mayar.id
- **Dashboard:** https://web.mayar.id

For technical issues, contact via dashboard support.

```

### references/integration-examples.md

```markdown
# Mayar Integration Examples

Real-world integration patterns for common use cases.

## WhatsApp E-Commerce Bot

### Complete Order Flow

```javascript
// 1. Customer sends order via WhatsApp
// "Mau order wedding template + maintenance 2 bulan"

// 2. Parse order and create invoice
const items = [
  { quantity: 1, rate: 750000, description: "Wedding Website Template" },
  { quantity: 2, rate: 1000000, description: "Monthly Maintenance" }
];

const total = items.reduce((sum, item) => sum + (item.quantity * item.rate), 0);
// Total: 2,750,000

// 3. Create invoice via mcporter
const invoice = execSync(`
  mcporter call mayar.create_invoice \\
    name="Customer Name" \\
    email="[email protected]" \\
    mobile="\\"6281234567890\\"" \\
    description="Wedding Template + Maintenance Package" \\
    redirectURL="https://yoursite.com/success" \\
    expiredAt="2026-02-07T23:59:59+07:00" \\
    items='${JSON.stringify(items)}' \\
    --output json
`);

const result = JSON.parse(invoice);
const paymentLink = result.data.link.replace('tsrlabs', 'yoursubdomain');

// 4. Format WhatsApp message
const message = `
āœ… *Order Confirmed!*

*Package:* Wedding Template + Maintenance

*Items:*
• Wedding Website Template
  Rp 750.000
• Monthly Maintenance x2
  Rp 2.000.000

*TOTAL: Rp 2.750.000*

šŸ’³ *Pembayaran:*
${paymentLink}

ā° Berlaku sampai: 7 Feb 2026

Terima kasih! šŸ™
`.trim();

// 5. Send via WhatsApp
message({
  action: 'send',
  channel: 'whatsapp',
  target: '+6281234567890',
  message: message
});
```

### Payment Status Polling

```javascript
// Poll every 30 seconds to check payment status
setInterval(async () => {
  const transactions = execSync(`
    mcporter call mayar.get_latest_transactions page:1 pageSize:10 --output json
  `);
  
  const data = JSON.parse(transactions);
  
  // Check if our invoice ID exists in paid transactions
  const paid = data.data.find(t => 
    t.paymentLinkId === invoiceId && t.status === 'paid'
  );
  
  if (paid) {
    // Send confirmation
    message({
      action: 'send',
      channel: 'whatsapp',
      target: customerPhone,
      message: `
āœ… *Pembayaran Berhasil!*

Invoice: #${paid.invoiceCode}
Amount: Rp ${paid.amount.toLocaleString('id-ID')}
Tanggal: ${paid.createdAt}

Setup akan segera dimulai. Terima kasih! šŸŽ‰
      `.trim()
    });
    
    clearInterval(this); // Stop polling
  }
}, 30000); // Every 30 seconds
```

## Service Marketplace

### Multi-Service Package

```bash
# Customer orders multiple services
mcporter call mayar.create_invoice \
  name="Business Client" \
  email="[email protected]" \
  mobile="\"6281234567890\"" \
  description="Digital Services Package" \
  redirectURL="https://yoursite.com/success" \
  expiredAt="2026-02-28T23:59:59+07:00" \
  items='[
    {"quantity":1,"rate":5000000,"description":"WhatsApp Bot Setup + CRM"},
    {"quantity":1,"rate":3000000,"description":"Website Development"},
    {"quantity":3,"rate":1000000,"description":"Monthly Maintenance"}
  ]'
```

Total: Rp 11,000,000 (5M + 3M + 3M)

### Recurring Billing Reminder

```javascript
// Check unpaid invoices and send reminders
const unpaid = execSync(`
  mcporter call mayar.get_latest_unpaid_transactions page:1 pageSize:50 --output json
`);

const overdue = JSON.parse(unpaid).data.filter(t => {
  const expiry = new Date(t.paymentLink.expiredAt);
  const soon = Date.now() + (24 * 60 * 60 * 1000); // 24 hours
  return expiry.getTime() < soon && expiry.getTime() > Date.now();
});

// Send reminder for each overdue invoice
overdue.forEach(invoice => {
  message({
    action: 'send',
    channel: 'whatsapp',
    target: invoice.customer.mobile,
    message: `
ā° *Reminder: Pembayaran akan segera kadaluarsa*

Invoice: #${invoice.invoiceCode}
Amount: Rp ${invoice.amount.toLocaleString('id-ID')}
Kadaluarsa: ${new Date(invoice.paymentLink.expiredAt).toLocaleString('id-ID')}

Link pembayaran:
${invoice.paymentLink.link}

Terima kasih! šŸ™
    `.trim()
  });
});
```

## Membership Site

### Check Active Members

```bash
mcporter call mayar.get_membership_customer_by_specific_product \
  productName:"Premium Membership" \
  productLink:"premium-membership" \
  productId:"your-product-uuid" \
  page:1 pageSize:100 \
  memberStatus:"active"
```

### Membership Renewal Automation

```javascript
// Get members expiring soon
const members = execSync(`
  mcporter call mayar.get_membership_customer_by_specific_product \
    productName:"Premium Membership" \
    productLink:"premium-membership" \
    productId:"uuid" \
    page:1 pageSize:100 \
    memberStatus:"active" \
    --output json
`);

const expiringSoon = JSON.parse(members).data.filter(member => {
  const expiry = new Date(member.expirationDate);
  const week = Date.now() + (7 * 24 * 60 * 60 * 1000);
  return expiry.getTime() < week;
});

// Create renewal invoice for each
expiringSoon.forEach(member => {
  const renewalInvoice = createInvoice({
    name: member.customerName,
    email: member.customerEmail,
    mobile: member.customerMobile,
    description: "Premium Membership Renewal",
    items: [
      { quantity: 1, rate: 500000, description: "Premium Membership - 1 Month" }
    ]
  });
  
  // Send renewal notification via email/WhatsApp
});
```

## Course/Digital Product Sales

### Course Purchase with Auto-Access

```javascript
// 1. Create invoice for course
const invoice = createInvoice({
  name: "Student Name",
  email: "[email protected]",
  mobile: "6281234567890",
  description: "Full Stack Development Course",
  items: [
    { quantity: 1, rate: 2500000, description: "Full Stack Dev Course" }
  ]
});

// 2. Monitor payment
pollPayment(invoice.data.id, async (paidInvoice) => {
  // 3. Grant course access
  await grantCourseAccess(paidInvoice.customer.email);
  
  // 4. Send access credentials
  message({
    action: 'send',
    channel: 'whatsapp',
    target: paidInvoice.customer.mobile,
    message: `
šŸŽ‰ *Pembayaran Berhasil!*

Kamu sekarang memiliki akses ke:
šŸ“š Full Stack Development Course

Login di: https://course.site/login
Email: ${paidInvoice.customer.email}
Password: (cek email)

Selamat belajar! šŸš€
    `.trim()
  });
});
```

## Donation Platform

### One-Time Donation

```bash
mcporter call mayar.create_invoice \
  name="Donor Name" \
  email="[email protected]" \
  mobile="\"6281234567890\"" \
  description="Donation for Education Program" \
  redirectURL="https://donation-site.com/thanks" \
  expiredAt="2026-12-31T23:59:59+07:00" \
  items='[{"quantity":1,"rate":100000,"description":"Education Fund Donation"}]'
```

### Donation with Custom Amount

```javascript
function createDonationInvoice(donorInfo, amount, program) {
  return createInvoice({
    name: donorInfo.name,
    email: donorInfo.email,
    mobile: donorInfo.phone,
    description: `Donation for ${program}`,
    items: [
      { quantity: 1, rate: amount, description: `${program} - Donation` }
    ]
  });
}

// Usage
const donation = createDonationInvoice(
  { name: "John Doe", email: "[email protected]", phone: "6281234567890" },
  500000,
  "Clean Water Project"
);
```

## Event Ticketing

### Multiple Ticket Types

```bash
mcporter call mayar.create_invoice \
  name="Attendee Name" \
  email="[email protected]" \
  mobile="\"6281234567890\"" \
  description="Tech Conference 2026 Tickets" \
  redirectURL="https://event-site.com/tickets" \
  expiredAt="2026-03-01T23:59:59+07:00" \
  items='[
    {"quantity":2,"rate":500000,"description":"Early Bird Ticket"},
    {"quantity":1,"rate":150000,"description":"Workshop Add-on"}
  ]'
```

Total: Rp 1,150,000 (2Ɨ500K + 1Ɨ150K)

### Post-Payment Ticket Delivery

```javascript
pollPayment(invoiceId, async (paidInvoice) => {
  // Generate QR code tickets
  const tickets = await generateTickets(paidInvoice);
  
  // Send tickets via WhatsApp
  tickets.forEach((ticket, index) => {
    message({
      action: 'send',
      channel: 'whatsapp',
      target: paidInvoice.customer.mobile,
      message: `
šŸŽŸļø *Ticket ${index + 1}*

Event: Tech Conference 2026
Date: 15 March 2026
Venue: JCC Hall A

Scan QR code at entrance:
${ticket.qrCodeUrl}

See you there! šŸŽ‰
      `.trim()
    });
  });
});
```

## Analytics & Reporting

### Daily Revenue Report

```javascript
const today = execSync(`
  mcporter call mayar.get_transactions_by_time_period \
    page:1 pageSize:100 \
    period:"today" \
    sortField:"createdAt" \
    sortOrder:"DESC" \
    --output json
`);

const transactions = JSON.parse(today).data;
const revenue = transactions.reduce((sum, t) => sum + t.amount, 0);
const count = transactions.length;

const report = `
šŸ“Š *Daily Revenue Report*
Date: ${new Date().toLocaleDateString('id-ID')}

šŸ’° Total Revenue: Rp ${revenue.toLocaleString('id-ID')}
šŸ“¦ Transactions: ${count}
šŸ’µ Average: Rp ${Math.round(revenue / count).toLocaleString('id-ID')}

Top 5 Sales:
${transactions.slice(0, 5).map(t => 
  `• ${t.paymentLinkDescription}: Rp ${t.amount.toLocaleString('id-ID')}`
).join('\n')}
`.trim();

// Send to admin
message({
  action: 'send',
  channel: 'whatsapp',
  target: '+628xxxx', // Admin number
  message: report
});
```

### Monthly Summary

```bash
mcporter call mayar.get_transactions_by_time_period \
  page:1 pageSize:1000 \
  period:"this_month" \
  sortField:"amount" \
  sortOrder:"DESC"
```

## Error Handling

### Retry Failed Invoices

```javascript
function createInvoiceWithRetry(data, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const invoice = execSync(`
        mcporter call mayar.create_invoice \
          name="${data.name}" \
          email="${data.email}" \
          mobile="\\"${data.mobile}\\"" \
          description="${data.description}" \
          redirectURL="${data.redirectURL}" \
          expiredAt="${data.expiredAt}" \
          items='${JSON.stringify(data.items)}' \
          --output json
      `);
      
      return JSON.parse(invoice);
    } catch (error) {
      console.error(`Attempt ${i + 1} failed:`, error.message);
      if (i === maxRetries - 1) throw error;
      
      // Wait before retry (exponential backoff)
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
    }
  }
}
```

### Handle Expired Invoices

```javascript
function recreateExpiredInvoice(oldInvoiceId) {
  // Get old invoice details
  const unpaid = execSync(`
    mcporter call mayar.get_latest_unpaid_transactions page:1 pageSize:100 --output json
  `);
  
  const oldInvoice = JSON.parse(unpaid).data.find(t => t.id === oldInvoiceId);
  
  if (!oldInvoice) {
    throw new Error('Invoice not found');
  }
  
  // Create new invoice with same details but new expiry
  const newExpiry = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
  
  return createInvoice({
    name: oldInvoice.paymentLinkName,
    email: oldInvoice.customer.email,
    mobile: oldInvoice.customer.mobile,
    description: oldInvoice.paymentLinkDescription,
    expiredAt: newExpiry,
    items: oldInvoice.items // Extract from original
  });
}
```

## Best Practices

### 1. Always Store Invoice IDs
```javascript
// Store mapping: customer → invoice ID
const invoiceDB = {
  [customerPhone]: {
    invoiceId: result.data.id,
    transactionId: result.data.transactionId,
    createdAt: Date.now(),
    status: 'pending'
  }
};
```

### 2. Set Reasonable Expiry Times
```javascript
// For immediate purchases: 24 hours
const expiry24h = new Date(Date.now() + 24 * 60 * 60 * 1000);

// For orders: 7 days
const expiry7d = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);

// For invoices: 30 days
const expiry30d = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
```

### 3. Format Phone Numbers Correctly
```javascript
function formatPhoneForMayar(phone) {
  // Remove all non-digits
  let cleaned = phone.replace(/\D/g, '');
  
  // Add country code if missing
  if (!cleaned.startsWith('62')) {
    cleaned = '62' + cleaned.replace(/^0+/, '');
  }
  
  return cleaned;
}
```

### 4. Handle Timezone Correctly
```javascript
// Always use Jakarta timezone for Indonesian customers
const expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
const expiryISO = expiry.toISOString().replace('Z', '+07:00');
```

```

### references/mcp-tools.md

```markdown
# Mayar MCP Tools Reference

Complete list of available tools via Mayar MCP server.

## Payment Generation

### create_invoice

Create itemized invoice with payment link.

**Input:**
- `name` (string, required) - Customer name
- `email` (string, required) - Customer email
- `mobile` (string, required) - Customer phone (format: "628xxx")
- `description` (string, required) - Order description
- `redirectURL` (string, required) - Post-payment redirect URL
- `expiredAt` (string, required) - Expiry date (ISO 8601 with timezone)
- `items` (array, required) - Items array:
  - `quantity` (number) - Item quantity
  - `rate` (number) - Price per unit (in Rupiah)
  - `description` (string) - Item name

**Output:**
- `id` - Invoice UUID
- `transactionId` - Transaction UUID
- `link` - Payment link URL
- `expiredAt` - Expiry timestamp (epoch ms)

**Example:**
```bash
mcporter call mayar.create_invoice \
  name="John Doe" \
  email="[email protected]" \
  mobile="\"6281234567890\"" \
  description="Product purchase" \
  redirectURL="https://example.com/thanks" \
  expiredAt="2026-12-31T23:59:59+07:00" \
  items='[{"quantity":1,"rate":100000,"description":"Product A"}]'
```

### send_portal_link

Send customer portal access link via email.

**Input:**
- `email` (string, required) - Customer email

**Output:**
- Success status

## Balance & Account

### get_balance

Get current account balance.

**Input:** None

**Output:**
- `balanceActive` - Available balance
- `balancePending` - Pending balance
- `balance` - Total balance

**Example:**
```bash
mcporter call mayar.get_balance
```

## Transaction Queries

### get_latest_transactions

Get recent paid transactions.

**Input:**
- `page` (number, required) - Page number (start from 1)
- `pageSize` (number, required) - Items per page

**Output:**
- `currentPage`, `totalPage`, `totalData`
- `data` - Array of transactions

### get_latest_unpaid_transactions

Get unpaid invoices.

**Input:**
- `page` (number, required)
- `pageSize` (number, required)

**Output:**
- Array of unpaid transactions

### get_transactions_by_time_period

Filter transactions by predefined periods.

**Input:**
- `page` (number, required)
- `pageSize` (number, required)
- `period` (string, required) - One of: `"today"`, `"this_week"`, `"this_month"`, `"this_year"`
- `sortField` (string, required) - `"createdAt"` or `"amount"`
- `sortOrder` (string, required) - `"ASC"` or `"DESC"`

**Example:**
```bash
mcporter call mayar.get_transactions_by_time_period \
  page:1 pageSize:10 \
  period:"this_month" \
  sortField:"createdAt" \
  sortOrder:"DESC"
```

### get_transactions_by_time_range

Filter transactions by custom date range.

**Input:**
- `page`, `pageSize` (numbers, required)
- `startAt` (string, required) - Start date (ISO 8601)
- `endAt` (string, required) - End date (ISO 8601)
- `sortField`, `sortOrder` (strings, required)

**Example:**
```bash
mcporter call mayar.get_transactions_by_time_range \
  page:1 pageSize:10 \
  startAt:"2026-01-01T00:00:00+07:00" \
  endAt:"2026-01-31T23:59:59+07:00" \
  sortField:"amount" \
  sortOrder:"DESC"
```

### get_transactions_by_customer_and_time_period

Filter by customer AND time period.

**Input:**
- `page`, `pageSize`, `period`, `sortField`, `sortOrder` (as above)
- `customerEmail` (string, required)
- `customerName` (string, required)

### get_transactions_by_customer_and_time_range

Filter by customer AND custom date range.

**Input:**
- `page`, `pageSize`, `startAt`, `endAt`, `sortField`, `sortOrder` (as above)
- `customerName`, `customerEmail` (strings, required)

### get_unpaid_transactions_by_time_range

Get unpaid transactions in date range.

**Input:**
- All fields from `get_transactions_by_time_range`
- Plus: `customerName`, `customerEmail` (strings, required)

## Customer Management

### get_customer_detail

Get customer transaction history.

**Input:**
- `customerName` (string, required)
- `customerEmail` (string, required)
- `page` (number, required)
- `pageSize` (number, required)

**Output:**
- Customer details + transaction list

## Product-Specific Queries

### get_transactions_by_specific_product

Get transactions for a specific product.

**Input:**
- `productName` (string, required)
- `productLink` (string, required)
- `productId` (string, required)
- `page`, `pageSize` (numbers, required)

### get_latest_transactions_by_customer

Filter by customer + product.

**Input:**
- `customerName`, `customerEmail` (strings, required)
- `productName`, `productLink` (strings, required)
- `page`, `pageSize` (numbers, required)

## Membership/Subscription Tools

### get_membership_customer_by_specific_product

Get membership customers for a product.

**Input:**
- `productName`, `productLink`, `productId` (strings, required)
- `page`, `pageSize` (numbers, required)
- `memberStatus` (string, required) - One of: `"active"`, `"inactive"`, `"finished"`, `"stopped"`

**Example:**
```bash
mcporter call mayar.get_membership_customer_by_specific_product \
  productName:"Premium Membership" \
  productLink:"premium-membership" \
  productId:"uuid-here" \
  page:1 pageSize:10 \
  memberStatus:"active"
```

### get_membership_customer_by_specific_product_and_tier

Filter membership by tier.

**Input:**
- All fields from above
- Plus: `membershipTierName`, `membershipTierId` (strings, required)

## Common Patterns

### Check if Invoice is Paid

```bash
# Get unpaid transactions
result=$(mcporter call mayar.get_latest_unpaid_transactions page:1 pageSize:50 --output json)

# Check if your invoice ID exists
# If NOT in unpaid list → it's paid!
```

### Get Today's Revenue

```bash
mcporter call mayar.get_transactions_by_time_period \
  page:1 pageSize:100 \
  period:"today" \
  sortField:"amount" \
  sortOrder:"DESC"
```

### Find Customer's Last Purchase

```bash
mcporter call mayar.get_customer_detail \
  customerName:"Customer Name" \
  customerEmail:"[email protected]" \
  page:1 pageSize:1
```

## Field Formats

**Phone Numbers:**
- Format: `"628xxxxxxxxxx"` (no + or spaces)
- Must be string with escaped quotes in CLI: `"\"628xxx\""`

**Dates (ISO 8601):**
- With timezone: `"2026-12-31T23:59:59+07:00"`
- UTC: `"2026-12-31T23:59:59Z"`

**Currency:**
- Always in Rupiah (integer)
- No decimal points
- Example: `100000` = Rp 100,000

**Status Values:**
- Invoice: `"created"` (unpaid), `"paid"`, `"expired"`, `"cancelled"`
- Member: `"active"`, `"inactive"`, `"finished"`, `"stopped"`

**Time Periods:**
- `"today"`, `"this_week"`, `"this_month"`, `"this_year"`

**Sort Fields:**
- `"createdAt"`, `"amount"`

**Sort Order:**
- `"ASC"` (ascending), `"DESC"` (descending)

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### _meta.json

```json
{
  "owner": "ahsanatha",
  "slug": "mayar-payment",
  "displayName": "Mayar Payment Integration",
  "latest": {
    "version": "1.0.0",
    "publishedAt": 1769837086941,
    "commit": "https://github.com/clawdbot/skills/commit/c4411263cf635cb4c1b9aae151dd4ee34b61856f"
  },
  "history": []
}

```