Skip to content

Receipt Management Guide

Learn how to create and manage receipts using Payme Subscribe API.

Overview

Receipts allow you to:

  • Create payment invoices
  • Pay with saved card tokens
  • Send invoices via SMS
  • Track payment status
  • Cancel unpaid receipts

Receipt Flow

1. receiptsCreate (create invoice)

2. receiptsPay (pay with card token)

3. receiptsCheck (verify payment)

4. receiptsGet (get details)

Creating Receipts

Basic Receipt

typescript
import { PaymeSubscribe } from '@joyida/payme';

const subscribeServer = new PaymeSubscribe({
  merchantId: process.env.PAYME_MERCHANT_ID!,
  password: process.env.PAYME_PASSWORD!
}, 'server');

const receipt = await subscribeServer.receiptsCreate({
  amount: 500000, // 5000 UZS in tiyin
  account: { order_id: '123' },
  description: 'Payment for order #123'
});

console.log('Receipt ID:', receipt.receipt._id);
console.log('State:', receipt.receipt.state); // 0 (waiting for payment)

Receipt with Fiscal Details

typescript
const receipt = await subscribeServer.receiptsCreate({
  amount: 500000,
  account: { order_id: '123' },
  description: 'Payment for order #123',
  detail: {
    receipt_type: 0,
    items: [
      {
        title: 'Product Name',
        price: 500000,
        count: 1,
        code: '00702001001000001', // IKPU code
        vat_percent: 15,
        package_code: '123456'
      }
    ]
  }
});

Receipt with Multiple Items

typescript
const receipt = await subscribeServer.receiptsCreate({
  amount: 1000000, // 10000 UZS
  account: { order_id: '123' },
  description: 'Order #123',
  detail: {
    receipt_type: 0,
    shipping: {
      title: 'Delivery',
      price: 50000 // 500 UZS
    },
    items: [
      {
        title: 'Product 1',
        price: 300000,
        count: 2,
        code: '00702001001000001',
        vat_percent: 15,
        package_code: '123456'
      },
      {
        title: 'Product 2',
        price: 400000,
        count: 1,
        code: '00702001001000002',
        vat_percent: 15,
        package_code: '123457'
      }
    ]
  }
});

Paying Receipts

Pay with Card Token

typescript
const payment = await subscribeServer.receiptsPay({
  id: receipt.receipt._id,
  token: 'card_token_here'
});

if (payment.receipt.state === 1) {
  console.log('✅ Payment successful!');
}

Pay with Payer Info

typescript
const payment = await subscribeServer.receiptsPay({
  id: receipt.receipt._id,
  token: 'card_token_here',
  payer: {
    phone: '998901234567'
  }
});

Sending Receipts

Send receipt invoice via SMS:

typescript
const result = await subscribeServer.receiptsSend({
  id: receipt.receipt._id,
  phone: '998901234567'
});

if (result.success) {
  console.log('✅ SMS sent to customer');
}

Checking Receipt Status

Quick Status Check

typescript
const status = await subscribeServer.receiptsCheck({
  id: receipt.receipt._id
});

console.log('Receipt state:', status.state);
// 0 = waiting for payment
// 1 = paid
// 2 = cancelled
// 3 = cancelled after payment

Get Full Receipt Details

typescript
const details = await subscribeServer.receiptsGet({
  id: receipt.receipt._id
});

console.log('Amount:', details.receipt.amount);
console.log('State:', details.receipt.state);
console.log('Description:', details.receipt.description);
console.log('Card:', details.receipt.card?.number);
console.log('Create time:', new Date(details.receipt.create_time));
console.log('Pay time:', new Date(details.receipt.pay_time));

Cancelling Receipts

Cancel Unpaid Receipt

typescript
const cancelled = await subscribeServer.receiptsCancel({
  id: receipt.receipt._id
});

console.log('Cancelled at:', new Date(cancelled.receipt.cancel_time));
console.log('State:', cancelled.receipt.state); // 2 (cancelled)

Cancel Paid Receipt (Refund)

typescript
// If receipt is already paid, cancellation will initiate refund
const refunded = await subscribeServer.receiptsCancel({
  id: receipt.receipt._id
});

console.log('State:', refunded.receipt.state); // 3 (cancelled after payment)

Getting All Receipts

Get Receipts for Period

typescript
const result = await subscribeServer.receiptsGetAll({
  from: Date.now() - 86400000, // 24 hours ago
  to: Date.now(),
  limit: 50
});

console.log('Found receipts:', result.receipts.length);

result.receipts.forEach(receipt => {
  console.log(`${receipt._id}: ${receipt.state} - ${receipt.amount} tiyin`);
});

Get Receipts with Pagination

typescript
const limit = 50;
let offset = 0;
let allReceipts = [];

while (true) {
  const result = await subscribeServer.receiptsGetAll({
    from: Date.now() - 86400000,
    to: Date.now(),
    offset,
    limit
  });
  
  allReceipts.push(...result.receipts);
  
  if (result.receipts.length < limit) {
    break; // No more receipts
  }
  
  offset += limit;
}

console.log('Total receipts:', allReceipts.length);

Receipt States

typescript
import { RECEIPT_STATES } from '@joyida/payme';

// State constants
RECEIPT_STATES.WAITING              // 0 - Waiting for payment
RECEIPT_STATES.PAID                 // 1 - Paid
RECEIPT_STATES.CANCELLED            // 2 - Cancelled
RECEIPT_STATES.CANCELLED_AFTER_PAY  // 3 - Cancelled after payment

// Usage
if (receipt.state === RECEIPT_STATES.PAID) {
  console.log('Receipt paid');
} else if (receipt.state === RECEIPT_STATES.WAITING) {
  console.log('Waiting for payment');
}

Complete Receipt Example

typescript
import { PaymeSubscribe, RECEIPT_STATES, PaymeError } from '@joyida/payme';

const subscribeServer = new PaymeSubscribe({
  merchantId: process.env.PAYME_MERCHANT_ID!,
  password: process.env.PAYME_PASSWORD!
}, 'server');

async function processReceiptPayment(
  orderId: string,
  amount: number,
  cardToken: string,
  customerPhone: string
) {
  try {
    // 1. Create receipt
    console.log('📝 Creating receipt...');
    const receipt = await subscribeServer.receiptsCreate({
      amount,
      account: { order_id: orderId },
      description: `Payment for order ${orderId}`,
      detail: {
        receipt_type: 0,
        items: [
          {
            title: 'Order Payment',
            price: amount,
            count: 1,
            code: '00702001001000001',
            vat_percent: 15,
            package_code: '123456'
          }
        ]
      }
    });
    
    console.log('✅ Receipt created:', receipt.receipt._id);
    
    // 2. Send SMS invoice (optional)
    console.log('📱 Sending SMS invoice...');
    await subscribeServer.receiptsSend({
      id: receipt.receipt._id,
      phone: customerPhone
    });
    console.log('✅ SMS sent');
    
    // 3. Pay with card token
    console.log('💳 Processing payment...');
    const payment = await subscribeServer.receiptsPay({
      id: receipt.receipt._id,
      token: cardToken,
      payer: {
        phone: customerPhone
      }
    });
    
    if (payment.receipt.state === RECEIPT_STATES.PAID) {
      console.log('✅ Payment successful!');
      
      // 4. Get full receipt details
      const details = await subscribeServer.receiptsGet({
        id: receipt.receipt._id
      });
      
      return {
        success: true,
        receiptId: details.receipt._id,
        amount: details.receipt.amount,
        card: details.receipt.card?.number,
        payTime: details.receipt.pay_time
      };
    } else {
      throw new Error('Payment failed');
    }
    
  } catch (error) {
    if (error instanceof PaymeError) {
      console.error('Payme error:', error.code, error.message);
      
      // Cancel receipt if payment failed
      try {
        await subscribeServer.receiptsCancel({
          id: receipt.receipt._id
        });
        console.log('Receipt cancelled');
      } catch (cancelError) {
        console.error('Failed to cancel receipt:', cancelError);
      }
    }
    throw error;
  }
}

// Usage
processReceiptPayment('ORD-123', 500000, 'card_token_here', '998901234567')
  .then(result => console.log('Payment result:', result))
  .catch(error => console.error('Payment error:', error));

Database Integration

Receipt Schema

sql
CREATE TABLE IF NOT EXISTS receipts (
  id TEXT PRIMARY KEY,
  order_id TEXT NOT NULL,
  amount INTEGER NOT NULL,
  state INTEGER NOT NULL DEFAULT 0,
  card_token TEXT,
  create_time INTEGER NOT NULL,
  pay_time INTEGER DEFAULT 0,
  cancel_time INTEGER DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_receipts_order_id ON receipts(order_id);
CREATE INDEX idx_receipts_state ON receipts(state);

Save Receipt

typescript
import { Database } from 'bun:sqlite';

const db = new Database('app.db');

function saveReceipt(data: {
  id: string;
  orderId: string;
  amount: number;
  state: number;
  createTime: number;
}) {
  db.run(`
    INSERT INTO receipts (id, order_id, amount, state, create_time)
    VALUES (?, ?, ?, ?, ?)
  `, [data.id, data.orderId, data.amount, data.state, data.createTime]);
}

function updateReceiptState(
  id: string,
  state: number,
  payTime?: number,
  cancelTime?: number
) {
  if (payTime) {
    db.run(`
      UPDATE receipts 
      SET state = ?, pay_time = ?, updated_at = CURRENT_TIMESTAMP
      WHERE id = ?
    `, [state, payTime, id]);
  } else if (cancelTime) {
    db.run(`
      UPDATE receipts 
      SET state = ?, cancel_time = ?, updated_at = CURRENT_TIMESTAMP
      WHERE id = ?
    `, [state, cancelTime, id]);
  }
}

Best Practices

✅ Do's

  1. Save receipt ID

    typescript
    saveReceipt({
      id: receipt.receipt._id,
      orderId: orderId,
      amount: amount,
      state: receipt.receipt.state,
      createTime: receipt.receipt.create_time
    });
  2. Check card token before payment

    typescript
    const cardCheck = await subscribeServer.cardsCheck({
      token: cardToken
    });
    
    if (!cardCheck.card.verify) {
      throw new Error('Card not verified');
    }
  3. Handle payment errors

    typescript
    try {
      await subscribeServer.receiptsPay(params);
    } catch (error) {
      // Cancel receipt if payment failed
      await subscribeServer.receiptsCancel({
        id: receipt.receipt._id
      });
    }
  4. Send SMS invoices

    typescript
    await subscribeServer.receiptsSend({
      id: receipt.receipt._id,
      phone: customerPhone
    });

❌ Don'ts

  1. Don't lose receipt ID
  2. Don't pay with unverified cards
  3. Don't forget to cancel failed receipts
  4. Don't skip error handling

Next Steps

Released under MIT License.