npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sikka/aps

v0.0.2

Published

A Stripe-like developer-friendly SDK for Amazon Payment Services integration. Supports payment links, hosted checkout, tokenization, webhooks, and payment management.

Downloads

195

Readme

Amazon Payment Services SDK

npm version License: MIT

A Stripe-like developer-friendly SDK for Amazon Payment Services integration


Features

  • 🚀 Stripe-like DX - Intuitive API design inspired by Stripe
  • ⚛️ React Hooks - useAPS, useCheckout, usePayment hooks for seamless React integration
  • 🧩 React Components - Pre-built components: HostedCheckoutButton, ErrorDisplay, PaymentStatus
  • 🔗 Payment Links - Generate shareable payment URLs via official APS API
  • 🛒 Hosted Checkout - Full-featured payment pages with 3DS support
  • 💳 Tokenization - Secure card storage for recurring payments
  • 🔔 Webhooks - Real-time payment event notifications with signature verification
  • 💰 Payment Management - Capture, refund, void, and query transactions
  • 🌍 Multi-Currency - Full support for Middle East currencies (SAR, AED, KWD, etc.)
  • 🔒 Secure - Proper SHA-256 signature calculation per APS documentation
  • 📦 TypeScript - Full TypeScript support with comprehensive types
  • 🧪 Testing Utilities - Mock client, test cards, and webhook helpers
  • 🛡️ Error Handling - Detailed error messages with suggested actions

Installation

npm install @sikka/aps

Quick Start

1. Initialize the Client

import APS from '@sikka/aps';

const aps = new APS({
  merchantId: process.env.APS_MERCHANT_ID,
  accessCode: process.env.APS_ACCESS_CODE,
  requestSecret: process.env.APS_REQUEST_SECRET,
  responseSecret: process.env.APS_RESPONSE_SECRET,
  environment: 'sandbox' // or 'production'
});

TypeScript Types

All types are exported from the package:

import type {
  APSConfig,
  PaymentResponse,
  PaymentLinkResponse,
  TokenizedCard,
  RefundResponse,
  CaptureResponse,
  VoidResponse,
  WebhookEvent,
  Order,
  Customer,
  TransactionStatus,
  PaymentMethod
} from '@sikka/aps';

2. Create a Payment Link

const link = await aps.paymentLinks.create({
  order: {
    id: 'order_123',
    amount: 10000, // 100.00 SAR (in fils/cents)
    currency: 'SAR',
    description: 'Premium Plan Subscription',
    customer: {
      email: '[email protected]',
      name: 'Ahmed Al-Saud',
      phone: '+966501234567'
    }
  },
  tokenValidityHours: 24 // Link expires in 24 hours
});

console.log('Payment URL:', link.url);
// Send this link to your customer via email, SMS, etc.

3. Create Hosted Checkout

const checkout = await aps.hostedCheckout.create({
  order: {
    id: 'order_456',
    amount: 25000, // 250.00 SAR
    currency: 'SAR',
    description: 'E-commerce Purchase',
    customer: {
      email: '[email protected]', // Required for hosted checkout
      name: 'Ahmed Al-Saud'
    }
  },
  returnUrl: 'https://yoursite.com/api/payment/return', // Use API route to handle POST data
  allowedPaymentMethods: ['card', 'apple_pay', 'mada']
});

// Redirect user to the hosted checkout page
if (checkout.redirectForm) {
  // Create a form and submit to redirectForm.url
  // The customer will be redirected back to returnUrl after payment
}

Important: Hosted Checkout uses return_url for all redirects (success, failure, or cancel).

⚠️ POST Data: APS sends the transaction result as a POST request (form data), not GET query parameters. You need an API route to receive the POST data:

// app/api/payment/return/route.ts
export async function POST(request: NextRequest) {
  const formData = await request.formData();
  
  // Convert to query parameters
  const params = new URLSearchParams();
  formData.forEach((value, key) => {
    if (typeof value === 'string') params.append(key, value);
  });
  
  // Redirect to result page
  return NextResponse.redirect(
    new URL(`/payment/result?${params.toString()}`, request.url)
  );
}

Then use the API route as your returnUrl:

returnUrl: 'https://yoursite.com/api/payment/return'

4. Custom Payment Page (Non-PCI)

Create a custom payment form while APS handles PCI compliance through tokenization.

// Step 1: Get tokenization form (backend)
const tokenizationForm = aps.customPaymentPage.getTokenizationForm({
  returnUrl: 'https://yoursite.com/api/token-result'
});

// Step 2: Render form in your frontend
// <form method="POST" action={tokenizationForm.url}>
//   {Object.entries(tokenizationForm.params).map(([key, value]) => (
//     <input type="hidden" name={key} value={value} />
//   ))}
//   <input name="card_number" placeholder="Card Number" required />
//   <input name="expiry_date" placeholder="MM/YY" required />
//   <input name="card_security_code" placeholder="CVV" required />
//   <input name="card_holder_name" placeholder="Card Holder Name" required />
//   <button type="submit">Pay</button>
// </form>

// Step 3: Handle response at returnUrl (backend)
// Response contains: token_name

// Step 4: Charge with token (backend)
const payment = await aps.customPaymentPage.chargeWithToken({
  order: {
    id: 'order_123',
    amount: 10000, // 100.00 SAR
    currency: 'SAR',
    description: 'Product Purchase'
  },
  tokenName: 'token_from_step_3',
  customerEmail: '[email protected]'
});

How it works:

  1. Get tokenization form from APS
  2. Render custom form with your branding
  3. Customer enters card details
  4. Form submits to APS (Non-PCI compliant)
  5. APS returns token to your returnUrl
  6. Use token to charge payments server-to-server
import { NextRequest, NextResponse } from 'next/server';
import { getAPSClient } from './aps-client';

export async function POST(request: NextRequest) {
  const body = await request.json();
  const signature = request.headers.get('x-aps-signature') || '';
  
  const aps = getAPSClient();
  
  // Verify webhook signature
  const event = aps.webhooks.constructEvent(body, signature);
  
  // Handle different event types
  switch (event.type) {
    case 'payment.success':
      // Update order status, send confirmation email, etc.
      console.log('Payment successful:', event.data);
      break;
    case 'payment.failed':
      // Notify customer, retry logic, etc.
      console.log('Payment failed:', event.data);
      break;
    case 'refund.success':
      // Process refund confirmation
      console.log('Refund processed:', event.data);
      break;
  }
  
  return NextResponse.json({ received: true });
}

7. Payment Management

// Capture an authorized payment (for two-step checkout)
const capture = await aps.payments.capture({
  transactionId: 'txn_123',
  amount: 10000 // Optional, defaults to full amount
});

// Refund a payment (full or partial)
const refund = await aps.payments.refund({
  transactionId: 'txn_123',
  amount: 5000, // Partial refund (50.00 SAR)
  reason: 'Customer request'
});

// Void an authorization (before capture)
const voided = await aps.payments.void({
  transactionId: 'txn_123',
  reason: 'Order cancelled'
});

// Query transaction status
const transaction = await aps.payments.query({
  transactionId: 'txn_123'
});

console.log('Transaction status:', transaction.status);

React Hooks & Components

Setup Provider

import { APSProvider } from '@sikka/aps/react';
import APS from '@sikka/aps';

const aps = new APS({
  merchantId: process.env.APS_MERCHANT_ID!,
  accessCode: process.env.APS_ACCESS_CODE!,
  requestSecret: process.env.APS_REQUEST_SECRET!,
  responseSecret: process.env.APS_RESPONSE_SECRET!,
  environment: 'sandbox'
});

function App() {
  return (
    <APSProvider client={aps}>
      <YourApp />
    </APSProvider>
  );
}

useAPS Hook

Access the APS client from any component:

import { useAPS } from '@sikka/aps/react';

function MyComponent() {
  const aps = useAPS();

  const handleClick = async () => {
    const link = await aps.paymentLinks.create({...});
    console.log(link.url);
  };
}

useCheckout Hook

Manage hosted checkout state:

import { useCheckout } from '@sikka/aps/react';

function CheckoutPage() {
  const { createCheckout, redirectToCheckout, isLoading, error } = useCheckout();

  const handleCheckout = async () => {
    await createCheckout({
      order: { id: 'order_123', amount: 10000, currency: 'SAR', customer: { email: '[email protected]' } },
      returnUrl: '/payment-result'
    });
    redirectToCheckout();
  };
}

usePayment Hook

Manage payment lifecycle:

import { usePayment } from '@sikka/aps/react';

function PaymentPage() {
  const { createPaymentLink, confirmPayment, status } = usePayment();

  const handlePayment = async () => {
    const link = await createPaymentLink({ order: {...} });
    window.open(link.url, '_blank');
  };

  // After customer returns
  const finalStatus = await confirmPayment({ transactionId: 'txn_123' });
}

HostedCheckoutButton Component

Complete checkout button with built-in state management:

import { HostedCheckoutButton } from '@sikka/aps/react';

<HostedCheckoutButton
  order={{
    id: 'order_123',
    amount: 10000,
    currency: 'SAR',
    customer: { email: '[email protected]' }
  }}
  returnUrl="https://yoursite.com/result"
>
  Pay Now
</HostedCheckoutButton>

ErrorDisplay Component

Display APS errors with helpful messages:

import { ErrorDisplay } from '@sikka/aps/react';

<ErrorDisplay
  error={error}
  onRetry={() => handleRetry()}
  onDismiss={() => setError(null)}
/>

PaymentStatus Component

Display payment status with icons:

import { PaymentStatus } from '@sikka/aps/react';

<PaymentStatus
  status="captured"
  amount={10000}
  currency="SAR"
  transactionId="txn_123"
/>

Error Handling

Get Error Details

import { getErrorDetails, isRetryableError } from '@sikka/aps';

try {
  await aps.paymentLinks.create({...});
} catch (error) {
  if (error.code) {
    const details = getErrorDetails(error.code);
    console.log(details.message);      // Human-readable message
    console.log(details.action);       // Suggested action
    console.log(details.category);     // Error category
    console.log(details.documentation); // Link to docs

    if (isRetryableError(error.code)) {
      await retryPayment();
    }
  }
}

Response Codes

import { ResponseCodes, ErrorCategories, categorizeError } from '@sikka/aps';

if (response.response_code === ResponseCodes.SUCCESS) {
  // Payment successful
} else if (response.response_code === ResponseCodes.INSUFFICIENT_FUNDS) {
  // Handle insufficient funds
}

const category = categorizeError(response.response_code);

Validation

import { validators } from '@sikka/aps';

// Validate individual fields
validators.isValidMerchantReference('order_123');
validators.isValidAmount(10000, 'SAR');
validators.isValidCardNumber('4111111111111111');

// Validate all at once
const result = validators.validatePaymentParams({
  merchant_reference: 'order_123',
  amount: 10000,
  currency: 'SAR',
  customer_email: '[email protected]'
});

if (!result.valid) {
  result.errors.forEach(err => console.log(err.field, err.error));
}

Testing

Mock Client

import { mockAPS } from '@sikka/aps/test';

const aps = mockAPS({ simulate: 'success', delay: 100 });
const link = await aps.paymentLinks.create({...});

// Simulate errors
const declinedAps = mockAPS({ simulate: 'declined' });
await expect(declinedAps.paymentLinks.create({...}))
  .rejects.toThrow('Transaction declined');

Test Cards

import { TestCards, testCards } from '@sikka/aps/test';

TestCards.VISA.SUCCESS        // '4111111111111111'
TestCards.VISA.DECLINED       // '4000000000000002'
TestCards.MADA.SUCCESS        // '5297410000000002'

testCards.visa.success;
testCards.visa.declined;

Mock Webhooks

import { createMockWebhookPayload, createMockSignature } from '@sikka/aps/test';

const payload = createMockWebhookPayload('payment.success', {
  merchant_reference: 'order_123',
  amount: '10000',
});

const signature = createMockSignature(payload, 'your-secret');

Complete API Reference

Configuration

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | merchantId | string | ✓ | - | Your APS merchant ID from dashboard | | accessCode | string | ✓ | - | API access code from Integration Settings | | requestSecret | string | ✓ | - | SHA Request Phrase for signing | | responseSecret | string | ✓ | - | SHA Response Phrase for verification | | environment | string | | 'sandbox' | 'sandbox' or 'production' | | currency | string | | 'USD' | Default currency code (e.g., 'SAR', 'AED') | | language | string | | 'en' | Interface language: 'en' or 'ar' |

Payment Links Module

Create payment links that can be shared via email, SMS, WhatsApp, etc.

await aps.paymentLinks.create(options: PaymentLinkOptions)

Options:

| Field | Type | Required | Description | |-------|------|----------|-------------| | order.id | string | | Unique order reference (auto-generated if not provided) | | order.amount | number | ✓ | Amount in fils/cents (e.g., 10000 = 100.00 SAR) | | order.currency | string | ✓ | Three-letter ISO currency code | | order.description | string | | Order description shown to customer | | order.customer.email | string | ✓ | Customer email for notifications | | order.customer.name | string | | Customer full name | | order.customer.phone | string | | Customer phone (international format) | | tokenValidityHours | number | | Link expiry in hours (default: 24) | | recurring | boolean | | Enable for recurring/subscription payments | | allowedPaymentMethods | array | | Restrict to specific methods: ['card', 'mada', 'apple_pay'] | | metadata | object | | Custom key-value pairs to attach to order |

Returns:

{
  url: string;              // Payment link URL to share
  linkId: string;           // APS payment link ID
  orderId: string;          // Your order reference
  expiresAt?: Date;         // Expiry timestamp
  rawResponse: object;      // Full APS API response
}

Hosted Checkout Module

Redirect customers to a secure APS-hosted payment page.

await aps.hostedCheckout.create(options: HostedCheckoutOptions)

Options:

| Field | Type | Required | Description | |-------|------|----------|-------------| | order.id | string | | Unique order reference (auto-generated if not provided) | | order.amount | number | ✓ | Amount in fils/cents (e.g., 10000 = 100.00 SAR) | | order.currency | string | ✓ | Three-letter ISO currency code | | order.description | string | | Order description shown to customer | | order.customer.email | string | ✓ | Required - Customer email for payment page | | order.customer.name | string | | Customer full name | | order.customer.phone | string | | Customer phone (international format) | | returnUrl | string | ✓ | Redirect URL after payment (success, failure, or cancel) | | tokenize | boolean | | Allow card saving for future payments (remember_me) | | allowedPaymentMethods | array | | Limit payment methods: ['card', 'mada', 'apple_pay'] | | hideShipping | boolean | | Hide shipping information fields |

Returns:

{
  transactionId: string;
  orderId: string;
  status: 'pending' | 'authorized' | 'captured' | 'failed';
  amount: number;
  currency: string;
  redirectForm: {
    url: string;        // APS payment page URL
    method: 'POST';
    params: Record<string, string>;  // All form parameters including signature
  };
  rawResponse: object;
}

Important Notes:

  • customer_email is required by APS
  • Use returnUrl (not successUrl/failureUrl) - APS redirects to this URL for all outcomes
  • The response contains a redirectForm that should be submitted via POST to redirect the customer

Tokenization Module

Securely tokenize card details for recurring payments.

await aps.tokens.create(options: TokenizeCardOptions)

Options:

| Field | Type | Required | Description | |-------|------|----------|-------------| | cardNumber | string | ✓ | Full card number | | expiryMonth | string | ✓ | 2-digit month (MM) | | expiryYear | string | ✓ | 2-digit year (YY) | | cvv | string | ✓ | Card security code | | cardholderName | string | | Name as shown on card |

Returns:

{
  token: string;        // Card token for future use
  last4: string;        // Last 4 digits
  brand: string;        // Card brand (visa, mastercard, mada, etc.)
  expiryMonth: string;
  expiryYear: string;
}

Additional Methods:

// Verify if token is still valid
const isValid = await aps.tokens.verify('tok_xxxxx');

// Delete/invalidate a token
await aps.tokens.delete('tok_xxxxx');

Custom Payment Page Module (Non-PCI)

Create custom payment forms while maintaining PCI compliance through tokenization.

// Get tokenization form
const form = aps.customPaymentPage.getTokenizationForm({
  returnUrl: 'https://yoursite.com/api/token-result',
  merchantReference: 'order_123'
});

// Render form in your frontend with custom styling
// Form submits to APS endpoint for secure tokenization

// After receiving token, charge with:
await aps.customPaymentPage.chargeWithToken({
  order: {
    id: string,
    amount: number,
    currency: string,
    description?: string
  },
  tokenName: string,        // Token from tokenization response
  customerEmail: string,    // Required
  customerName?: string,
  customerPhone?: string,
  returnUrl?: string
});

Returns (chargeWithToken):

{
  transactionId: string;
  orderId: string;
  status: 'captured' | 'authorized' | 'pending' | 'failed';
  amount: number;
  currency: string;
  paymentMethod?: string;
  authenticationUrl?: string;  // 3DS URL if required
  redirectForm?: { ... };      // For 3DS redirect
  message?: string;
  rawResponse: object;
}

Important Notes:

  • Tokenization form collects card details securely (Non-PCI compliant)
  • Card data submits directly to APS, never touches your server
  • Token received at returnUrl can be used for immediate or future charges
  • Use chargeWithToken() for server-to-server payment processing

Payments Module (Management)

Manage existing transactions.

// Capture authorized payment
await aps.payments.capture({
  transactionId: string,
  amount?: number  // Optional, full capture if not specified
});

// Refund payment
await aps.payments.refund({
  transactionId: string,
  amount?: number,  // Optional, full refund if not specified
  reason?: string
});

// Void authorization
await aps.payments.void({
  transactionId: string,
  reason?: string
});

// Query transaction
await aps.payments.query({
  transactionId?: string,
  orderId?: string
});

Webhooks Module

Verify and parse webhook events.

// In your API route handler
const signature = request.headers.get('x-aps-signature') || '';
const payload = request.body;

// Verify signature
const isValid = aps.webhooks.verifySignature(
  JSON.stringify(payload),
  signature
);

if (!isValid) {
  return res.status(401).send('Invalid signature');
}

// Construct event
const event = aps.webhooks.constructEvent(payload);

// Handle by type
switch (event.type) {
  case 'payment.success':
  case 'payment.failed':
  case 'payment.pending':
  case 'refund.success':
  case 'refund.failed':
  case 'chargeback.created':
  case 'subscription.renewed':
  case 'subscription.cancelled':
}

Webhook Event Structure:

{
  id: string;
  type: WebhookEventType;
  timestamp: Date;
  data: {
    transactionId: string;
    orderId: string;
    amount: number;
    currency: string;
    status: TransactionStatus;
    paymentMethod?: string;
    metadata?: Record<string, any>;
  };
  rawPayload: object;
}

Supported Payment Methods

| Method | Regions | Description | |--------|---------|-------------| | card | All | Visa, Mastercard, American Express | | apple_pay | All | Apple Pay | | mada | Saudi Arabia | MADA debit cards | | stc_pay | Saudi Arabia | STC Pay wallet | | knet | Kuwait | KNET payment | | naps | Qatar | NAPS payment | | fawry | Egypt | Fawry cash payment | | meeza | Egypt | Meeza cards | | sadad | Saudi Arabia | Sadad payment | | aman | Egypt | Aman installment |

Environment Variables

Create a .env.local file:

# APS Merchant Credentials (from your APS dashboard)
APS_MERCHANT_ID=your_merchant_id
APS_ACCESS_CODE=your_access_code
APS_REQUEST_SECRET=your_request_secret
APS_RESPONSE_SECRET=your_response_secret

# APS Configuration
APS_ENVIRONMENT=sandbox          # Use 'production' for live
APS_CURRENCY=SAR                 # Default currency
APS_LANGUAGE=en                  # Default language

# Your App URL
NEXT_PUBLIC_APP_URL=http://localhost:3000

Supported Currencies

  • SAR - Saudi Riyal
  • AED - UAE Dirham
  • KWD - Kuwaiti Dinar
  • QAR - Qatari Riyal
  • BHD - Bahraini Dinar
  • OMR - Omani Rial
  • JOD - Jordanian Dinar
  • EGP - Egyptian Pound
  • USD - US Dollar
  • EUR - Euro

Error Handling

import { APSException, APSError } from '@sikka/aps';

try {
  const link = await aps.paymentLinks.create({ ... });
} catch (error) {
  if (error instanceof APSException) {
    console.error('Error Code:', error.code);
    console.error('Message:', error.message);
    console.error('Status:', error.statusCode);
    console.error('Details:', error.rawResponse);
    
    // Handle specific errors
    switch (error.code) {
      case 'PAYMENT_LINK_ERROR':
        // Handle payment link creation failure
        break;
      case 'SIGNATURE_ERROR':
        // Handle signature mismatch
        break;
      case 'INVALID_CREDENTIALS':
        // Handle authentication failure
        break;
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

Common Response Codes

| Code | Message | Description | |------|---------|-------------| | 00000 | Success | Transaction successful | | 48000 | Success | Payment link created successfully | | 10030 | Authentication failed | 3DS authentication failed | | 00008 | Signature mismatch | Invalid signature | | 00002 | Invalid parameter | Parameter format error | | 14000 | Declined | Card declined by issuer |

Security Best Practices

  1. Never expose credentials - Always use environment variables
  2. Server-side only - All APS API calls must be server-side
  3. Verify webhooks - Always verify webhook signatures
  4. Use HTTPS - Required for production
  5. PCI Compliance - Use hosted checkout or payment links to avoid PCI scope
  6. Validate amounts - Always validate amounts server-side before creating payments

Testing

Sandbox Environment

const aps = new APS({
  // ... credentials
  environment: 'sandbox'
});

Test Cards (use in sandbox):

| Card Number | Type | CVV | Expiry | |-------------|------|-----|--------| | 4111 1111 1111 1111 | Visa | 123 | 12/25 | | 5297 4100 0000 0002 | MADA | 123 | 12/25 | | 5100 0000 0000 0008 | Mastercard | 123 | 12/25 |

Production

const aps = new APS({
  // ... production credentials
  environment: 'production'
});

Next.js Integration Example

See the included test app for a complete working example:

# Run the test app
pnpm dev

Visit http://localhost:3000 to test payment creation and http://localhost:3000/docs for documentation.

License

MIT License - see LICENSE for details.

Support

For APS-specific issues, contact Amazon Payment Services merchant support: