diff --git a/MODULAR PRODUCTION-GRADE SDK b/MODULAR PRODUCTION-GRADE SDK new file mode 100644 index 0000000..1cf173a --- /dev/null +++ b/MODULAR PRODUCTION-GRADE SDK @@ -0,0 +1,33 @@ +STRUKTUR FINAL (PRODUCTION SDK) + +pi-sdk-js-pro/ +├─ src/ +│ ├─ core/ +│ │ ├─ config.ts +│ │ ├─ event-bus.ts +│ │ ├─ payment-engine.ts +│ │ +│ ├─ auth/ +│ │ └─ connect.ts +│ │ +│ ├─ payment/ +│ │ └─ create-payment.ts +│ │ +│ ├─ marketplace/ +│ │ └─ merchant.ts +│ │ +│ ├─ plugins/ +│ │ └─ plugin.ts +│ │ +│ ├─ audit/ +│ │ └─ audit-log.ts +│ │ +│ ├─ types/ +│ │ └─ index.ts +│ │ +│ ├─ sdk.ts +│ └─ index.ts +│ +├─ package.json +├─ tsconfig.json +└─ README.md diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..cf4c1e4 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,38 @@ +export type PiEnv = 'production' | 'sandbox' | 'mock' + +export interface PiUser { + uid: string + username: string + roles?: string[] +} + +export type PaymentStatus = + | 'CREATED' + | 'APPROVED' + | 'SUBMITTED' + | 'COMPLETED' + | 'CANCELLED' + | 'FAILED' + | 'EXPIRED' + +export interface PaymentData { + amount: number + memo: string + metadata?: Record +} + +export interface SplitRule { + merchantId: string + amount: number +} + +export interface MarketplacePayment extends PaymentData { + splits?: SplitRule[] + escrow?: boolean +} + +export interface Merchant { + merchantId: string + wallet: string + feePercent?: number +} diff --git a/src/types/src/core/config.ts b/src/types/src/core/config.ts new file mode 100644 index 0000000..884d0a4 --- /dev/null +++ b/src/types/src/core/config.ts @@ -0,0 +1,12 @@ +import { PiEnv } from '../types' + +export interface PiSdkConfig { + env?: PiEnv + appId?: string + debug?: boolean +} + +export const defaultConfig: PiSdkConfig = { + env: 'production', + debug: false +} diff --git a/src/types/src/core/src/core/event-bus.ts b/src/types/src/core/src/core/event-bus.ts new file mode 100644 index 0000000..fc47351 --- /dev/null +++ b/src/types/src/core/src/core/event-bus.ts @@ -0,0 +1,15 @@ +export type EventHandler = (payload?: any) => void + +export class EventBus { + private listeners = new Map() + + on(event: string, handler: EventHandler) { + const list = this.listeners.get(event) || [] + list.push(handler) + this.listeners.set(event, list) + } + + emit(event: string, payload?: any) { + this.listeners.get(event)?.forEach(h => h(payload)) + } +} diff --git a/src/types/src/core/src/core/src/core/event-bus.ts b/src/types/src/core/src/core/src/core/event-bus.ts new file mode 100644 index 0000000..fc47351 --- /dev/null +++ b/src/types/src/core/src/core/src/core/event-bus.ts @@ -0,0 +1,15 @@ +export type EventHandler = (payload?: any) => void + +export class EventBus { + private listeners = new Map() + + on(event: string, handler: EventHandler) { + const list = this.listeners.get(event) || [] + list.push(handler) + this.listeners.set(event, list) + } + + emit(event: string, payload?: any) { + this.listeners.get(event)?.forEach(h => h(payload)) + } +} diff --git a/src/types/src/core/src/core/src/core/src/audit/audit-log.ts b/src/types/src/core/src/core/src/core/src/audit/audit-log.ts new file mode 100644 index 0000000..50630fa --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/audit-log.ts @@ -0,0 +1,8 @@ +import { PaymentStatus } from '../types' + +export interface AuditLog { + paymentId: string + status: PaymentStatus + timestamp: number + payload?: any +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/payment-engine.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/payment-engine.ts new file mode 100644 index 0000000..02461e8 --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/payment-engine.ts @@ -0,0 +1,37 @@ +import { PaymentStatus } from '../types' +import { AuditLog } from '../audit/audit-log' +import { EventBus } from './event-bus' + +export class PaymentEngine { + private status: PaymentStatus = 'CREATED' + private audit: AuditLog[] = [] + + constructor( + private events: EventBus, + private debug = false + ) {} + + transition(status: PaymentStatus, payload?: any) { + this.status = status + this.audit.push({ + paymentId: payload?.paymentId ?? 'unknown', + status, + timestamp: Date.now(), + payload + }) + + if (this.debug) { + console.log('[PiSDK][STATE]', status, payload) + } + + this.events.emit(`payment:${status.toLowerCase()}`, payload) + } + + getAuditTrail() { + return this.audit + } + + getStatus() { + return this.status + } +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/plugin.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/plugin.ts new file mode 100644 index 0000000..edc2d7b --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/plugin.ts @@ -0,0 +1,7 @@ +import { PiSdk } from '../sdk' + +export interface PiSdkPlugin { + name: string + onInit?(sdk: PiSdk): void + onPaymentEvent?(event: string, payload: any): void +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/merchant.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/merchant.ts new file mode 100644 index 0000000..adb1445 --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/merchant.ts @@ -0,0 +1,13 @@ +import { Merchant } from '../types' + +export class MerchantRegistry { + private merchants = new Map() + + register(merchant: Merchant) { + this.merchants.set(merchant.merchantId, merchant) + } + + get(id: string) { + return this.merchants.get(id) + } +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/connect.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/connect.ts new file mode 100644 index 0000000..71e30ae --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/connect.ts @@ -0,0 +1,24 @@ +import { PiUser } from '../types' + +declare global { + interface Window { + Pi: any + } +} + +export async function connectPi(env: string): Promise { + if (env === 'mock') { + return { uid: 'mock', username: 'MockUser' } + } + + if (!window.Pi) { + throw new Error('Pi SDK not loaded') + } + + const auth = await window.Pi.authenticate( + ['payments', 'username'], + (err: any) => { if (err) throw err } + ) + + return auth.user +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/create-payment.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/create-payment.ts new file mode 100644 index 0000000..7993a89 --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/create-payment.ts @@ -0,0 +1,36 @@ +import { MarketplacePayment } from '../types' +import { PaymentEngine } from '../core/payment-engine' + +declare global { + interface Window { + Pi: any + } +} + +export function createPayment( + data: MarketplacePayment, + engine: PaymentEngine +) { + engine.transition('CREATED', data) + + window.Pi.createPayment( + { + amount: data.amount, + memo: data.memo, + metadata: data.metadata + }, + { + onReadyForServerApproval: (paymentId: string) => + engine.transition('APPROVED', { paymentId }), + + onReadyForServerCompletion: (paymentId: string) => + engine.transition('SUBMITTED', { paymentId }), + + onCancel: (paymentId: string) => + engine.transition('CANCELLED', { paymentId }), + + onError: (error: any, payment?: any) => + engine.transition('FAILED', { error, payment }) + } + ) +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/src/sdk.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/src/sdk.ts new file mode 100644 index 0000000..e2cfe38 --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/src/sdk.ts @@ -0,0 +1,50 @@ +import { PiSdkConfig, defaultConfig } from './core/config' +import { EventBus } from './core/event-bus' +import { PaymentEngine } from './core/payment-engine' +import { MerchantRegistry } from './marketplace/merchant' +import { PiSdkPlugin } from './plugins/plugin' +import { MarketplacePayment, PiUser } from './types' +import { connectPi } from './auth/connect' +import { createPayment } from './payment/create-payment' + +export class PiSdk { + static user: PiUser | null = null + + private events = new EventBus() + private engine: PaymentEngine + private merchants = new MerchantRegistry() + private plugins: PiSdkPlugin[] = [] + private config: PiSdkConfig + + constructor(config?: PiSdkConfig) { + this.config = { ...defaultConfig, ...config } + this.engine = new PaymentEngine(this.events, this.config.debug) + } + + async connect() { + PiSdk.user = await connectPi(this.config.env!) + this.events.emit('user:connected', PiSdk.user) + return PiSdk.user + } + + on(event: string, handler: any) { + this.events.on(event, handler) + } + + use(plugin: PiSdkPlugin) { + plugin.onInit?.(this) + this.plugins.push(plugin) + } + + registerMerchant(m: any) { + this.merchants.register(m) + } + + pay(data: MarketplacePayment) { + createPayment(data, this.engine) + } + + audit() { + return this.engine.getAuditTrail() + } +} diff --git a/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/src/src/index.ts b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/src/src/index.ts new file mode 100644 index 0000000..e28e54c --- /dev/null +++ b/src/types/src/core/src/core/src/core/src/audit/src/core/src/plugins/src/marketplace/src/auth/src/payment/src/src/index.ts @@ -0,0 +1,2 @@ +export * from './sdk' +export * from './types'