This commit adds comprehensive COD support using Saleor's native Transaction system:
**Architecture:**
- Uses Saleor's native Transaction objects (not metadata)
- Modular payment method configuration
- Extensible design for future payment types (cards, bank transfer, etc.)
**New Components:**
- PaymentMethodSelector: Reusable payment method selection UI
- PaymentMethodCard: Individual payment method card
- CODInstructions: COD-specific instructions and guidance
- PaymentSection: Checkout integration wrapper
**Core Features:**
- COD selected by default for Serbia (default-channel)
- Transaction created automatically on order completion
- Transaction visible in Saleor Dashboard
- Multi-language support (EN, SR, DE, FR)
- No additional fees
- Instructions shown to customer (prepare cash, inspect order, no fee)
**Files Added:**
- docs/COD-IMPLEMENTATION-PLAN.md
- src/lib/config/paymentMethods.ts
- src/lib/saleor/payments/types.ts
- src/lib/saleor/payments/cod.ts
- src/components/payment/PaymentMethodSelector.tsx
- src/components/payment/PaymentMethodCard.tsx
- src/components/payment/CODInstructions.tsx
- src/components/payment/index.ts
- src/app/[locale]/checkout/components/PaymentSection.tsx
**Files Modified:**
- src/app/[locale]/checkout/page.tsx (added payment section, transaction creation)
- src/i18n/messages/{en,sr,de,fr}.json (payment translations)
**Technical Details:**
- Transaction status: NOT_CHARGED
- Available actions: [CHARGE]
- PSP Reference format: COD-{orderNumber}-{timestamp}
- Staff collects cash and fulfills order via Dashboard
Closes: Cash on Delivery payment implementation
107 lines
2.6 KiB
TypeScript
107 lines
2.6 KiB
TypeScript
/**
|
|
* Payment methods configuration
|
|
* Centralized configuration for all available payment methods
|
|
*/
|
|
|
|
import type { PaymentMethod, Money } from '@/lib/saleor/payments/types';
|
|
|
|
/**
|
|
* List of all available payment methods
|
|
* Configure availability per channel, fees, and other settings
|
|
*/
|
|
export const paymentMethods: PaymentMethod[] = [
|
|
{
|
|
id: 'cod',
|
|
name: 'Cash on Delivery',
|
|
description: 'Pay when you receive your order',
|
|
type: 'simple',
|
|
fee: 0,
|
|
available: true,
|
|
availableInChannels: ['default-channel'], // Currently Serbia only
|
|
icon: 'Banknote',
|
|
},
|
|
{
|
|
id: 'card',
|
|
name: 'Credit Card',
|
|
description: 'Secure online payment',
|
|
type: 'app',
|
|
fee: 0,
|
|
available: false, // Coming soon
|
|
availableInChannels: ['default-channel'],
|
|
icon: 'CreditCard',
|
|
},
|
|
{
|
|
id: 'bank_transfer',
|
|
name: 'Bank Transfer',
|
|
description: 'Pay via bank transfer',
|
|
type: 'simple',
|
|
fee: 0,
|
|
available: false, // Coming later
|
|
availableInChannels: ['default-channel'],
|
|
icon: 'Building2',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Get payment methods available for a specific channel
|
|
*/
|
|
export function getPaymentMethodsForChannel(channel: string): PaymentMethod[] {
|
|
return paymentMethods.filter(
|
|
(method) =>
|
|
method.available && method.availableInChannels.includes(channel)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get a specific payment method by ID
|
|
*/
|
|
export function getPaymentMethodById(id: string): PaymentMethod | undefined {
|
|
return paymentMethods.find((method) => method.id === id);
|
|
}
|
|
|
|
/**
|
|
* Check if a payment method is available for a channel
|
|
*/
|
|
export function isPaymentMethodAvailable(
|
|
methodId: string,
|
|
channel: string
|
|
): boolean {
|
|
const method = getPaymentMethodById(methodId);
|
|
if (!method) return false;
|
|
return method.available && method.availableInChannels.includes(channel);
|
|
}
|
|
|
|
/**
|
|
* Default payment method ID
|
|
* Used when no payment method is explicitly selected
|
|
*/
|
|
export const DEFAULT_PAYMENT_METHOD = 'cod';
|
|
|
|
/**
|
|
* Channel configuration
|
|
* Maps channels to their supported payment methods
|
|
*/
|
|
export const channelPaymentConfig: Record<string, string[]> = {
|
|
'default-channel': ['cod'], // Serbia - COD only for now
|
|
};
|
|
|
|
/**
|
|
* Format payment method fee for display
|
|
*/
|
|
export function formatPaymentFee(fee: number, currency: string): string {
|
|
if (fee === 0) return 'No additional fee';
|
|
return new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: currency,
|
|
}).format(fee);
|
|
}
|
|
|
|
/**
|
|
* Generate PSP reference for COD transactions
|
|
* Format: COD-{orderNumber}-{timestamp}
|
|
*/
|
|
export function generateCODReference(orderNumber: string): string {
|
|
const timestamp = Date.now();
|
|
return `COD-${orderNumber}-${timestamp}`;
|
|
}
|