Files
Portfolio/blog-posts/97-payment-integration-providers.md
T
damjan_savicandClaude Opus 4.5 43484c5023 Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics
- Add .env.example for environment configuration
- Add accessibility and lighthouse audit scripts
- Remove obsolete SEO reports and temporary files
- Remove dev-dist build artifacts and backup files
- Remove unused portrait images (moved/consolidated elsewhere)
- Update contact form and component improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:42:11 +01:00

704 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Payment Integration (Multiple Providers)
**Meta-Description:** Payment Provider Integration. Stripe, PayPal, Klarna und lokale Zahlungsmethoden für globale E-Commerce Anwendungen.
**Keywords:** Payment Integration, Stripe, PayPal, Klarna, Payment Gateway, Checkout, E-Commerce, Payment Methods
---
## Einführung
**Globale E-Commerce** erfordert multiple Payment Provider. Kunden erwarten ihre bevorzugte Zahlungsmethode von **Kreditkarte** über **PayPal** bis **Klarna**. Dieser Guide zeigt die Integration verschiedener Provider in einer einheitlichen Architektur.
---
## Payment Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ PAYMENT PROVIDER ABSTRACTION │
├─────────────────────────────────────────────────────────────┤
│ │
│ Unified Interface: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PaymentService │ │
│ │ ├── createPayment(order, method) │ │
│ │ ├── capturePayment(paymentId) │ │
│ │ ├── refundPayment(paymentId, amount) │ │
│ │ └── getPaymentStatus(paymentId) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Provider Adapters: │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Stripe │ │ PayPal │ │ Klarna │ │ Local │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ ↓ ↓ ↓ ↓ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Stripe │ │ PayPal │ │ Klarna │ │ SEPA/ │ │
│ │ API │ │ API │ │ API │ │ iDEAL/etc│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Payment Methods by Region: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Germany: Klarna, SEPA, Giropay, Credit Card │ │
│ │ Netherlands: iDEAL, Credit Card, PayPal │ │
│ │ USA: Credit Card, PayPal, Apple Pay, Google Pay │ │
│ │ UK: Credit Card, PayPal, Klarna │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## Payment Service Interface
```typescript
// lib/payments/types.ts
export interface PaymentMethod {
id: string;
type: PaymentMethodType;
name: string;
icon: string;
countries: string[];
currencies: string[];
minAmount?: number;
maxAmount?: number;
}
export type PaymentMethodType =
| 'card'
| 'paypal'
| 'klarna'
| 'sepa_debit'
| 'ideal'
| 'giropay'
| 'sofort'
| 'bancontact'
| 'eps'
| 'apple_pay'
| 'google_pay';
export interface CreatePaymentInput {
orderId: string;
amount: number;
currency: string;
method: PaymentMethodType;
customer: {
email: string;
name?: string;
phone?: string;
};
billing?: Address;
shipping?: Address;
metadata?: Record<string, string>;
returnUrl: string;
cancelUrl?: string;
}
export interface PaymentResult {
id: string;
provider: string;
status: PaymentStatus;
redirectUrl?: string;
clientSecret?: string;
error?: string;
}
export type PaymentStatus =
| 'pending'
| 'processing'
| 'requires_action'
| 'succeeded'
| 'failed'
| 'canceled'
| 'refunded';
export interface PaymentProvider {
name: string;
supportedMethods: PaymentMethodType[];
createPayment(input: CreatePaymentInput): Promise<PaymentResult>;
capturePayment(paymentId: string): Promise<PaymentResult>;
refundPayment(paymentId: string, amount?: number): Promise<PaymentResult>;
getPaymentStatus(paymentId: string): Promise<PaymentStatus>;
handleWebhook(payload: unknown, signature: string): Promise<void>;
}
```
---
## Stripe Adapter
```typescript
// lib/payments/providers/stripe.ts
import Stripe from 'stripe';
import { PaymentProvider, CreatePaymentInput, PaymentResult, PaymentStatus } from '../types';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-06-20'
});
export class StripeProvider implements PaymentProvider {
name = 'stripe';
supportedMethods: PaymentMethodType[] = [
'card', 'sepa_debit', 'ideal', 'giropay',
'sofort', 'bancontact', 'eps', 'klarna'
];
async createPayment(input: CreatePaymentInput): Promise<PaymentResult> {
try {
// Map method to Stripe payment method type
const paymentMethodTypes = this.mapPaymentMethod(input.method);
const session = await stripe.checkout.sessions.create({
mode: 'payment',
payment_method_types: paymentMethodTypes,
customer_email: input.customer.email,
line_items: [
{
price_data: {
currency: input.currency.toLowerCase(),
unit_amount: input.amount,
product_data: {
name: `Order ${input.orderId}`
}
},
quantity: 1
}
],
success_url: input.returnUrl,
cancel_url: input.cancelUrl || input.returnUrl,
metadata: {
orderId: input.orderId,
...input.metadata
}
});
return {
id: session.payment_intent as string,
provider: 'stripe',
status: 'pending',
redirectUrl: session.url!
};
} catch (error) {
return {
id: '',
provider: 'stripe',
status: 'failed',
error: (error as Error).message
};
}
}
async capturePayment(paymentId: string): Promise<PaymentResult> {
const paymentIntent = await stripe.paymentIntents.capture(paymentId);
return {
id: paymentIntent.id,
provider: 'stripe',
status: this.mapStatus(paymentIntent.status)
};
}
async refundPayment(paymentId: string, amount?: number): Promise<PaymentResult> {
const refund = await stripe.refunds.create({
payment_intent: paymentId,
amount
});
return {
id: refund.id,
provider: 'stripe',
status: refund.status === 'succeeded' ? 'refunded' : 'pending'
};
}
async getPaymentStatus(paymentId: string): Promise<PaymentStatus> {
const paymentIntent = await stripe.paymentIntents.retrieve(paymentId);
return this.mapStatus(paymentIntent.status);
}
async handleWebhook(payload: string, signature: string): Promise<void> {
const event = stripe.webhooks.constructEvent(
payload,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
switch (event.type) {
case 'payment_intent.succeeded':
await this.handlePaymentSucceeded(event.data.object as Stripe.PaymentIntent);
break;
case 'payment_intent.payment_failed':
await this.handlePaymentFailed(event.data.object as Stripe.PaymentIntent);
break;
}
}
private mapPaymentMethod(method: PaymentMethodType): Stripe.Checkout.SessionCreateParams.PaymentMethodType[] {
const mapping: Record<PaymentMethodType, Stripe.Checkout.SessionCreateParams.PaymentMethodType[]> = {
card: ['card'],
sepa_debit: ['sepa_debit'],
ideal: ['ideal'],
giropay: ['giropay'],
sofort: ['sofort'],
bancontact: ['bancontact'],
eps: ['eps'],
klarna: ['klarna'],
paypal: ['paypal'],
apple_pay: ['card'],
google_pay: ['card']
};
return mapping[method] || ['card'];
}
private mapStatus(status: Stripe.PaymentIntent.Status): PaymentStatus {
const mapping: Record<string, PaymentStatus> = {
requires_payment_method: 'pending',
requires_confirmation: 'pending',
requires_action: 'requires_action',
processing: 'processing',
requires_capture: 'processing',
canceled: 'canceled',
succeeded: 'succeeded'
};
return mapping[status] || 'pending';
}
}
```
---
## PayPal Adapter
```typescript
// lib/payments/providers/paypal.ts
import { PaymentProvider, CreatePaymentInput, PaymentResult } from '../types';
const PAYPAL_API = process.env.PAYPAL_MODE === 'live'
? 'https://api-m.paypal.com'
: 'https://api-m.sandbox.paypal.com';
export class PayPalProvider implements PaymentProvider {
name = 'paypal';
supportedMethods: PaymentMethodType[] = ['paypal'];
private async getAccessToken(): Promise<string> {
const auth = Buffer.from(
`${process.env.PAYPAL_CLIENT_ID}:${process.env.PAYPAL_SECRET}`
).toString('base64');
const response = await fetch(`${PAYPAL_API}/v1/oauth2/token`, {
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials'
});
const data = await response.json();
return data.access_token;
}
async createPayment(input: CreatePaymentInput): Promise<PaymentResult> {
const accessToken = await this.getAccessToken();
const response = await fetch(`${PAYPAL_API}/v2/checkout/orders`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
intent: 'CAPTURE',
purchase_units: [
{
reference_id: input.orderId,
amount: {
currency_code: input.currency.toUpperCase(),
value: (input.amount / 100).toFixed(2)
}
}
],
application_context: {
return_url: input.returnUrl,
cancel_url: input.cancelUrl || input.returnUrl,
brand_name: 'Your Store',
user_action: 'PAY_NOW'
}
})
});
const order = await response.json();
if (order.error) {
return {
id: '',
provider: 'paypal',
status: 'failed',
error: order.error.message
};
}
const approveLink = order.links.find((l: any) => l.rel === 'approve');
return {
id: order.id,
provider: 'paypal',
status: 'pending',
redirectUrl: approveLink?.href
};
}
async capturePayment(paymentId: string): Promise<PaymentResult> {
const accessToken = await this.getAccessToken();
const response = await fetch(
`${PAYPAL_API}/v2/checkout/orders/${paymentId}/capture`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
);
const result = await response.json();
return {
id: result.id,
provider: 'paypal',
status: result.status === 'COMPLETED' ? 'succeeded' : 'failed'
};
}
async refundPayment(paymentId: string, amount?: number): Promise<PaymentResult> {
const accessToken = await this.getAccessToken();
// Get capture ID from order
const orderResponse = await fetch(
`${PAYPAL_API}/v2/checkout/orders/${paymentId}`,
{
headers: { 'Authorization': `Bearer ${accessToken}` }
}
);
const order = await orderResponse.json();
const captureId = order.purchase_units[0]?.payments?.captures?.[0]?.id;
if (!captureId) {
return {
id: '',
provider: 'paypal',
status: 'failed',
error: 'No capture found'
};
}
const refundResponse = await fetch(
`${PAYPAL_API}/v2/payments/captures/${captureId}/refund`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: amount ? JSON.stringify({
amount: {
value: (amount / 100).toFixed(2),
currency_code: order.purchase_units[0].amount.currency_code
}
}) : undefined
}
);
const refund = await refundResponse.json();
return {
id: refund.id,
provider: 'paypal',
status: refund.status === 'COMPLETED' ? 'refunded' : 'pending'
};
}
async getPaymentStatus(paymentId: string): Promise<PaymentStatus> {
const accessToken = await this.getAccessToken();
const response = await fetch(
`${PAYPAL_API}/v2/checkout/orders/${paymentId}`,
{
headers: { 'Authorization': `Bearer ${accessToken}` }
}
);
const order = await response.json();
const statusMap: Record<string, PaymentStatus> = {
CREATED: 'pending',
SAVED: 'pending',
APPROVED: 'requires_action',
COMPLETED: 'succeeded',
VOIDED: 'canceled'
};
return statusMap[order.status] || 'pending';
}
async handleWebhook(payload: unknown, signature: string): Promise<void> {
// Verify webhook signature
// Process webhook events
}
}
```
---
## Payment Service
```typescript
// lib/payments/service.ts
import { StripeProvider } from './providers/stripe';
import { PayPalProvider } from './providers/paypal';
import { PaymentProvider, CreatePaymentInput, PaymentResult, PaymentMethodType } from './types';
import { db } from '@/lib/db';
const providers: Record<string, PaymentProvider> = {
stripe: new StripeProvider(),
paypal: new PayPalProvider()
};
function getProviderForMethod(method: PaymentMethodType): PaymentProvider {
for (const provider of Object.values(providers)) {
if (provider.supportedMethods.includes(method)) {
return provider;
}
}
throw new Error(`No provider supports payment method: ${method}`);
}
export async function createPayment(input: CreatePaymentInput): Promise<PaymentResult> {
const provider = getProviderForMethod(input.method);
const result = await provider.createPayment(input);
// Store payment record
await db.payment.create({
data: {
id: result.id,
orderId: input.orderId,
provider: provider.name,
method: input.method,
amount: input.amount,
currency: input.currency,
status: result.status,
metadata: input.metadata
}
});
return result;
}
export async function capturePayment(paymentId: string): Promise<PaymentResult> {
const payment = await db.payment.findUnique({ where: { id: paymentId } });
if (!payment) throw new Error('Payment not found');
const provider = providers[payment.provider];
const result = await provider.capturePayment(paymentId);
await db.payment.update({
where: { id: paymentId },
data: { status: result.status }
});
return result;
}
export async function refundPayment(
paymentId: string,
amount?: number
): Promise<PaymentResult> {
const payment = await db.payment.findUnique({ where: { id: paymentId } });
if (!payment) throw new Error('Payment not found');
const provider = providers[payment.provider];
const result = await provider.refundPayment(paymentId, amount);
await db.payment.update({
where: { id: paymentId },
data: {
status: result.status,
refundedAmount: amount || payment.amount
}
});
return result;
}
export function getAvailablePaymentMethods(
country: string,
currency: string,
amount: number
): PaymentMethod[] {
const methods: PaymentMethod[] = [
{
id: 'card',
type: 'card',
name: 'Credit/Debit Card',
icon: '/icons/card.svg',
countries: ['*'],
currencies: ['*']
},
{
id: 'paypal',
type: 'paypal',
name: 'PayPal',
icon: '/icons/paypal.svg',
countries: ['*'],
currencies: ['EUR', 'USD', 'GBP']
},
{
id: 'klarna',
type: 'klarna',
name: 'Klarna',
icon: '/icons/klarna.svg',
countries: ['DE', 'AT', 'NL', 'SE', 'FI', 'NO', 'DK', 'GB', 'US'],
currencies: ['EUR', 'USD', 'GBP', 'SEK', 'NOK', 'DKK'],
minAmount: 100,
maxAmount: 500000
},
{
id: 'ideal',
type: 'ideal',
name: 'iDEAL',
icon: '/icons/ideal.svg',
countries: ['NL'],
currencies: ['EUR']
},
{
id: 'giropay',
type: 'giropay',
name: 'Giropay',
icon: '/icons/giropay.svg',
countries: ['DE'],
currencies: ['EUR']
},
{
id: 'sepa_debit',
type: 'sepa_debit',
name: 'SEPA Direct Debit',
icon: '/icons/sepa.svg',
countries: ['DE', 'AT', 'NL', 'BE', 'FR', 'ES', 'IT'],
currencies: ['EUR']
}
];
return methods.filter(method => {
const countryMatch = method.countries.includes('*') || method.countries.includes(country);
const currencyMatch = method.currencies.includes('*') || method.currencies.includes(currency);
const amountMatch = (!method.minAmount || amount >= method.minAmount) &&
(!method.maxAmount || amount <= method.maxAmount);
return countryMatch && currencyMatch && amountMatch;
});
}
```
---
## Checkout UI
```tsx
// components/checkout/PaymentMethodSelector.tsx
'use client';
import { useState } from 'react';
import Image from 'next/image';
import { PaymentMethod } from '@/lib/payments/types';
interface Props {
methods: PaymentMethod[];
selected: string | null;
onSelect: (methodId: string) => void;
}
export function PaymentMethodSelector({ methods, selected, onSelect }: Props) {
return (
<div className="space-y-3">
<h3 className="font-semibold">Payment Method</h3>
<div className="grid gap-2">
{methods.map((method) => (
<button
key={method.id}
onClick={() => onSelect(method.id)}
className={`flex items-center gap-3 p-4 border rounded-lg transition ${
selected === method.id
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<Image
src={method.icon}
alt={method.name}
width={40}
height={24}
className="object-contain"
/>
<span className="font-medium">{method.name}</span>
{selected === method.id && (
<span className="ml-auto text-blue-500"></span>
)}
</button>
))}
</div>
</div>
);
}
```
---
## Provider Comparison
| Provider | Card | PayPal | Klarna | Local Methods | Fees |
|----------|------|--------|--------|---------------|------|
| **Stripe** | ✓ | ✓ | ✓ | iDEAL, SEPA, etc. | 1.4%+ |
| **PayPal** | ✓ | ✓ | - | - | 2.9%+ |
| **Adyen** | ✓ | ✓ | ✓ | 250+ methods | Custom |
| **Mollie** | ✓ | ✓ | ✓ | iDEAL, Bancontact | 1.8%+ |
---
## Fazit
Payment Integration erfordert:
1. **Abstraction Layer**: Einheitliches Interface
2. **Multiple Providers**: Regionale Methoden
3. **Error Handling**: Graceful Degradation
4. **Webhooks**: Asynchrone Statusupdates
Die richtige Payment-Strategie erhöht Conversion.
---
## Bildprompts
1. "Payment method selection UI, card icons and options"
2. "Checkout flow diagram, multiple payment providers"
3. "Payment dashboard, transaction history and analytics"
---
## Quellen
- [Stripe Payment Methods](https://stripe.com/docs/payments/payment-methods)
- [PayPal Checkout](https://developer.paypal.com/docs/checkout/)
- [Klarna Integration](https://docs.klarna.com/)
- [Payment Provider Comparison](https://www.merchantmaverick.com/)