UNPKG

@sahabaplus/moyasar

Version:

A comprehensive TypeScript SDK for integrating with the Moyasar payment gateway

762 lines (616 loc) 18.3 kB
# Moyasar SDK A comprehensive TypeScript SDK for integrating with the Moyasar payment gateway. This SDK provides type-safe access to Moyasar's APIs for payments, invoices, and webhook management. ## Features - **Type-safe**: Full TypeScript support with comprehensive type definitions - **Event-driven**: Advanced event system for webhook handling - **Modular**: Clean architecture with separation of concerns - **Multi-platform**: Support for web, mobile wallets (Apple Pay, Google Pay, Samsung Pay), and STC Pay - **Payment Methods**: Credit cards, tokenized payments, and digital wallets - **Invoice Management**: Create, update, and manage invoices - **Webhook Support**: Real-time event notifications with built-in validation - **Well-tested**: Comprehensive test suite with high coverage ## Installation ```bash npm install @sahabaplus/moyasar ``` ## Quick Start ```typescript import { MoyasarClient } from "@sahabaplus/moyasar"; const moyasar = new MoyasarClient({ apiKey: "sk_your_api_key" }); // Create a payment const payment = await moyasar.payment.create({ amount: 1000, // Amount in smallest currency unit (e.g., halalas for SAR) currency: "SAR", description: "Test payment", callback_url: "https://your-app.com/callback", source: { type: "creditcard", name: "John Doe", number: "4111111111111111", month: 12, year: 2025, cvc: "123" } }); console.log("Payment created:", payment.id); ``` ## API Reference ### MoyasarClient The main client class that provides access to all Moyasar services. ```typescript const moyasar = new MoyasarClient({ apiKey: "sk_your_api_key", baseUrl: "https://api.moyasar.com", // Optional, defaults to production timeout: 30000 // Optional, defaults to 30 seconds }); ``` #### Client Methods - `ping()` - Test API connectivity - `getClientInfo()` - Get client version and configuration info ### Payment Service Handle payment operations including creation, capture, refund, and more. #### Create Payment ```typescript // Credit Card Payment const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Order #12345", callback_url: "https://your-app.com/callback", source: { type: "creditcard", name: "Ahmed Mohammed", number: "4111111111111111", month: 12, year: 2025, cvc: "123", "3ds": true, // Enable 3D Secure manual: false // Auto-capture }, metadata: { order_id: "12345", customer_id: "cust_123" } }); // Apple Pay Payment const applePayPayment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Apple Pay Purchase", callback_url: "https://your-app.com/callback", source: { type: "applepay", token: "apple_pay_token_here" } }); // STC Pay Payment const stcPayPayment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "STC Pay Purchase", callback_url: "https://your-app.com/callback", source: { type: "stcpay", mobile: "966501234567" } }); ``` #### Payment Operations ```typescript // Retrieve payment details const payment = await moyasar.payment.retrieve("pay_123"); // List payments const payments = await moyasar.payment.list({ limit: 10, offset: 0, status: "paid" }); // Update payment const updated = await moyasar.payment.update({ paymentId: "pay_123", update: { description: "Updated description", metadata: { updated: true } } }); // Capture authorized payment const captured = await moyasar.payment.capture({ paymentId: "pay_123", capture: { amount: 1000 // Optional, captures full amount if not specified } }); // Refund payment const refunded = await moyasar.payment.refund({ paymentId: "pay_123", refund: { amount: 500, // Partial refund reason: "Customer requested refund" } }); // Void payment const voided = await moyasar.payment.void("pay_123"); ``` #### Advanced Payment Queries ```typescript // Get payments by status const paidPayments = await moyasar.payment.getPaid({ limit: 10 }); const failedPayments = await moyasar.payment.getFailed({ limit: 10 }); const authorizedPayments = await moyasar.payment.getAuthorized({ limit: 10 }); // Search by metadata const payments = await moyasar.payment.searchByMetadata({ metadata: { order_id: "12345" }, options: { limit: 10 } }); // Get by card last 4 digits const cardPayments = await moyasar.payment.getByCardLast4({ last4: "1111", options: { limit: 10 } }); // Get by RRN (Retrieval Reference Number) const rrnPayments = await moyasar.payment.getByRRN({ rrn: "123456789012", options: { limit: 10 } }); // Check payment capabilities const capabilities = await moyasar.payment.getPaymentCapabilities("pay_123"); console.log("Can refund:", capabilities.canRefund); console.log("Max refund amount:", capabilities.maxRefundAmount); ``` ### Invoice Service Manage invoices and billing. #### Create Invoice ```typescript const invoice = await moyasar.invoice.create({ amount: 1000, currency: "SAR", description: "Monthly subscription", callback_url: "https://your-app.com/callback", metadata: { subscription_id: "sub_123" } }); // Bulk create invoices const invoices = await moyasar.invoice.createBulk({ invoices: [ { amount: 1000, currency: "SAR", description: "Invoice 1", callback_url: "https://your-app.com/callback" }, { amount: 2000, currency: "SAR", description: "Invoice 2", callback_url: "https://your-app.com/callback" } ] }); ``` #### Invoice Operations ```typescript // Retrieve invoice details const invoice = await moyasar.invoice.retrieve("inv_123"); // List invoices const invoices = await moyasar.invoice.list({ limit: 10, offset: 0, status: "paid" }); // Update invoice const updated = await moyasar.invoice.update("inv_123", { description: "Updated description" }); // Cancel invoice await moyasar.invoice.cancel("inv_123"); ``` #### Advanced Invoice Queries ```typescript // Get invoices by status const paidInvoices = await moyasar.invoice.getPaid({ limit: 10 }); const expiredInvoices = await moyasar.invoice.getExpired({ limit: 10 }); // Search by metadata const invoices = await moyasar.invoice.searchByMetadata( { subscription_id: "sub_123" }, { limit: 10 } ); ``` ### Webhook Service Handle real-time event notifications from Moyasar. #### Webhook Management ```typescript // Create webhook const webhook = await moyasar.webhook.create({ url: "https://your-app.com/webhooks/moyasar", http_method: "post", events: ["payment_paid", "payment_failed", "payment_refunded"] }); // List webhooks const webhooks = await moyasar.webhook.list(); // Retrieve webhook details const webhook = await moyasar.webhook.retrieve("webhook_123"); // Update webhook const updated = await moyasar.webhook.update("webhook_123", { events: ["payment_paid", "payment_failed"] }); // Delete webhook await moyasar.webhook.delete("webhook_123"); // Get available webhook events const events = await moyasar.webhook.availableEvents(); console.log("Available events:", events); ``` #### Webhook Attempts ```typescript // List webhook attempts const attempts = await moyasar.webhook.attempts.list({ limit: 10, offset: 0 }); // Retrieve specific attempt const attempt = await moyasar.webhook.attempts.retrieve("attempt_123"); ``` #### Processing Webhooks The SDK provides a robust `processWebhook()` method that handles parsing, validation, and signature verification: ```typescript import { MoyasarClient } from "@sahabaplus/moyasar"; const moyasar = new MoyasarClient({ apiKey: "sk_your_api_key" }); // In your webhook endpoint (e.g., Express.js) app.post("/webhooks/moyasar", async (req, res) => { try { // Process and verify webhook const payload = await moyasar.webhook.processWebhook( req.body, // Can be string, Buffer, or parsed object { signature: req.headers["x-moyasar-signature"], secret: "your_webhook_secret" } ); // If succeeded, all `moyasar.webhook.on*` events will be triggered when matched with the webhook event. console.log("Webhook event:", payload.type); console.log("Webhook data:", payload.data); res.status(200).send("OK"); } catch (error) { console.error("Webhook processing failed:", error); res.status(400).send("Invalid webhook"); } }); ``` #### Event Handling The SDK provides a type-safe event emitter for handling webhook events: ```typescript // Listen to specific events moyasar.webhook.onPaymentEvent("payment_paid", (payload) => { console.log("Payment successful:", payload.data); }); moyasar.webhook.onPaymentEvent("payment_failed", (payload) => { console.log("Payment failed:", payload.data); }); // Listen to all payment events moyasar.webhook.onAnyPaymentEvent((payload) => { console.log("Payment event:", payload.type, payload.data); }); // Using standard event emitter syntax moyasar.webhook.on("payment_paid", (payload) => { console.log("Payment received:", payload); }); ``` #### Available Events - `payment_paid` - Payment completed successfully - `payment_failed` - Payment failed - `payment_authorized` - Payment authorized (manual capture) - `payment_captured` - Payment captured - `payment_refunded` - Payment refunded - `payment_voided` - Payment voided - `payment_abandoned` - Payment abandoned - `payment_verified` - Payment verified - `payment_canceled` - Payment canceled - `payment_expired` - Payment expired - `balance_transferred` - Balance transferred - `payout_initiated` - Payout initiated - `payout_paid` - Payout completed - `payout_failed` - Payout failed - `payout_canceled` - Payout canceled - `payout_returned` - Payout returned ## Payment Methods ### Credit Cards Supports all major card schemes: - Visa - Mastercard - American Express - Mada (Saudi domestic cards) ```typescript const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Credit card payment", callback_url: "https://your-app.com/callback", source: { type: "creditcard", name: "Card Holder Name", number: "4111111111111111", month: 12, year: 2025, cvc: "123", "3ds": true, // Enable 3D Secure manual: false, // Auto-capture save_card: true // Save card for future use } }); ``` ### Digital Wallets #### Apple Pay ```typescript const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Apple Pay payment", callback_url: "https://your-app.com/callback", source: { type: "applepay", token: "apple_pay_token_from_client" } }); ``` #### Google Pay ```typescript const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Google Pay payment", callback_url: "https://your-app.com/callback", source: { type: "googlepay", token: "google_pay_token_from_client" } }); ``` #### Samsung Pay ```typescript const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Samsung Pay payment", callback_url: "https://your-app.com/callback", source: { type: "samsungpay", token: "samsung_pay_token_from_client" } }); ``` ### STC Pay Saudi Telecom's digital wallet service. ```typescript const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "STC Pay payment", callback_url: "https://your-app.com/callback", source: { type: "stcpay", mobile: "966501234567", cashier_id: "cashier_123", // Optional branch: "branch_456" // Optional } }); ``` ### Tokenized Payments Use saved payment methods for recurring payments. ```typescript const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Token payment", callback_url: "https://your-app.com/callback", source: { type: "token", token: "token_1234567890abcdef", cvc: "123" // Optional for saved cards } }); ``` ## Error Handling The SDK provides comprehensive error handling with specific error types: ```typescript import { MoyasarError, PaymentError, InvoiceError, WebhookError, WebhookValidationError } from "@sahabaplus/moyasar"; try { const payment = await moyasar.payment.create({ // payment data }); } catch (error) { if (error instanceof PaymentError) { console.log("Payment error:", error.message); console.log("HTTP status:", error.statusCode); } else if (error instanceof InvoiceError) { console.log("Invoice error:", error.message); } else if (error instanceof WebhookError) { console.log("Webhook error:", error.message); } else if (error instanceof MoyasarError) { console.log("API error:", error.message); } else { console.log("Unexpected error:", error); } } ``` ## Configuration ### Environment Options ```typescript const moyasar = new MoyasarClient({ apiKey: "sk_your_api_key", baseUrl: "https://api.moyasar.com", // Production (default) // baseUrl: "https://sandbox.moyasar.com", // Sandbox timeout: 30000, // Request timeout in milliseconds retries: 3, // Number of retries for failed requests retryDelay: 1000 // Delay between retries in milliseconds }); ``` ### TypeSafe Metadata You can safely define your metadata type to guarantee type safety during development. Furthermore, your can pass a custom metadata validator to ensure runtime safety for your metadata. ```typescript import { MoyasarClient } from "@sahabaplus/moyasar"; type MyMetadata = { order_id: string; customer_id: string; internal_ref: number; }; const moyasar = new MoyasarClient<MyMetadata>({ apiKey: "sk_your_api_key" }); const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Order payment", callback_url: "https://your-app.com/callback", source: { type: "creditcard", /* ... */ }, metadata: { order_id: "12345", customer_id: "cust_123" } // <- Type error, missing `internal_ref` property }); ``` You can also use a custom metadata validator to ensure runtime safety for your metadata. ```typescript import { MoyasarClient } from "@sahabaplus/moyasar"; import { z } from "zod"; // Define your metadata schema const MyMetadataSchema = z.object({ order_id: z.string(), customer_id: z.string(), internal_ref: z.number().optional() }); type MyMetadata = z.infer<typeof MyMetadataSchema>; const moyasar = new MoyasarClient<MyMetadata>({ apiKey: "sk_your_api_key", metadataValidator: { parse: (data) => MyMetadataSchema.parse(data) } }); // Now all metadata is type-safe const payment = await moyasar.payment.create({ amount: 1000, currency: "SAR", description: "Order payment", callback_url: "https://your-app.com/callback", source: { type: "creditcard", /* ... */ }, metadata: { order_id: "12345", customer_id: "cust_123", internal_ref: 999 } }); ``` ## Testing ```bash # Run tests npm test # Run tests with coverage npm run test:coverage # Run integration tests npm run test:integration ``` ## Examples ### Complete Payment Flow with Error Handling ```typescript import { MoyasarClient, PaymentError } from "@sahabaplus/moyasar"; const moyasar = new MoyasarClient({ apiKey: "sk_your_api_key" }); async function processPayment() { try { // Create payment const payment = await moyasar.payment.create({ amount: 10000, // 100.00 SAR currency: "SAR", description: "Order #12345", callback_url: "https://myapp.com/callback", source: { type: "creditcard", name: "Ahmed Mohammed", number: "4111111111111111", month: 12, year: 2025, cvc: "123", "3ds": true } }); console.log("Payment created:", payment.id); // Check capabilities const capabilities = await moyasar.payment.getPaymentCapabilities(payment.id); if (capabilities.canRefund) { // Perform partial refund const refunded = await moyasar.payment.refund({ paymentId: payment.id, refund: { amount: 2000, // Refund 20.00 SAR reason: "Partial order cancellation" } }); console.log("Refund successful:", refunded.refunded_amount); } } catch (error) { if (error instanceof PaymentError) { console.error("Payment failed:", error.message); } else { console.error("Unexpected error:", error); } } } processPayment(); ``` ### Webhook Server Example (Express.js) ```typescript import express from "express"; import { MoyasarClient, WebhookError } from "@sahabaplus/moyasar"; const app = express(); app.use(express.json()); const moyasar = new MoyasarClient({ apiKey: "sk_your_api_key" }); // Setup event listeners moyasar.webhook.onPaymentEvent("payment_paid", async (payload) => { console.log("Payment received:", payload.data.id); // Update your database, send confirmation email, etc. }); moyasar.webhook.onPaymentEvent("payment_failed", async (payload) => { console.log("Payment failed:", payload.data.id); // Notify customer, log failure, etc. }); // Webhook endpoint app.post("/webhooks/moyasar", async (req, res) => { try { const signature = req.headers["x-moyasar-signature"]; // Process webhook with automatic validation await moyasar.webhook.processWebhook(req.body, { signature: signature as string, secret: process.env.WEBHOOK_SECRET! }); res.status(200).send("OK"); } catch (error) { console.error("Webhook error:", error); res.status(400).send("Invalid webhook"); } }); app.listen(3000, () => { console.log("Webhook server running on port 3000"); }); ``` ## Contributing 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ## License MIT License - see the [LICENSE](LICENSE) file for details. ## Support - [Official Moyasar Documentation](https://docs.moyasar.com/) - [GitHub Issues](https://github.com/sahabaplus/moyasar/issues) - [Email Support](mailto:support@sahabaplus.com) ## Changelog See [CHANGELOG.md](CHANGELOG.md) for version history and updates.