@@ -223,12 +236,12 @@ import { RozoPayButton } from "@rozoai/intent-pay";
appId={APP_ID}
toChain={parsedConfig.chainId}
toAddress={
- isEvmChain(parsedConfig.chainId)
+ isEvm
? (getAddress(parsedConfig.recipientAddress) as Address)
: parsedConfig.recipientAddress
}
toToken={
- isEvmChain(parsedConfig.chainId)
+ isEvm
? (getAddress(parsedConfig.tokenAddress) as Address)
: parsedConfig.tokenAddress
}
@@ -283,7 +296,7 @@ import { RozoPayButton } from "@rozoai/intent-pay";
defaultRecipientAddress={config.recipientAddress}
/>
- {parsedConfig && isStellarChain(parsedConfig.chainId) && (
+ {parsedConfig && isStellar && (
ℹ️ Stellar Deposit Configuration
@@ -306,7 +319,7 @@ import { RozoPayButton } from "@rozoai/intent-pay";
)}
- {parsedConfig && isSolanaChain(parsedConfig.chainId) && (
+ {parsedConfig && isSolana && (
ℹ️ Solana Deposit Configuration
diff --git a/package.json b/package.json
index 68daf93eb..31edfe53c 100644
--- a/package.json
+++ b/package.json
@@ -5,18 +5,20 @@
"description": "",
"main": "index.js",
"scripts": {
- "build": "pnpm --filter @rozoai/intent-common build && pnpm --filter @rozoai/intent-pay build",
- "dev": "pnpm -r --parallel --filter '@rozoai/*' dev",
- "dev:common": "pnpm --filter @rozoai/intent-common build --watch",
- "dev:pay": "pnpm --filter @rozoai/intent-pay dev",
- "dev:example": "pnpm --filter @rozoai/pay-nextjs-app-example dev",
- "install:local": "pnpm install",
+ "build": "bun run build:common && bun run build:pay",
+ "build:common": "cd packages/pay-common && bun run build",
+ "build:pay": "cd packages/connectkit && bun run build",
+ "dev": "bun run dev:common & bun run dev:pay & bun run dev:example",
+ "dev:common": "cd packages/pay-common && bun run dev",
+ "dev:pay": "cd packages/connectkit && bun run dev",
+ "dev:example": "cd examples/nextjs-app && bun run dev",
+ "install:local": "bun install",
"prepare": "husky",
- "release": "pnpm build && pnpm --filter @rozoai/intent-common publish && pnpm --filter @rozoai/intent-pay release",
- "clean": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +",
- "clean:full": "pnpm clean && rm -f bun.lock",
- "clean:deps": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && find . -name 'dist' -type d -prune -exec rm -rf '{}' + && find . -name 'build' -type d -prune -exec rm -rf '{}' + && find . -name '.next' -type d -prune -exec rm -rf '{}' +",
- "clean:all": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' + && find . -name 'dist' -type d -prune -exec rm -rf '{}' + && find . -name 'build' -type d -prune -exec rm -rf '{}' + && find . -name '.next' -type d -prune -exec rm -rf '{}' + && rm -f pnpm-lock.yaml"
+ "release": "bun run build && cd packages/pay-common && bun publish && cd ../../packages/connectkit && bun run release",
+ "clean": "bun x rimraf **/node_modules",
+ "clean:full": "bun run clean && rm -f bun.lock",
+ "clean:deps": "bun x rimraf **/node_modules **/dist **/build **/.next",
+ "clean:all": "bun run clean:deps && rm -f bun.lock && rm -f pnpm-lock.yaml"
},
"lint-staged": {
"packages/connectkit/**/*.{js,jsx,ts,tsx}": [
@@ -39,5 +41,25 @@
"packages/*",
"examples/nextjs-app"
],
+ "pnpm": {
+ "overrides": {
+ "zod": "^3.25.76",
+ "@stellar/stellar-sdk": "^14.4.3",
+ "rollup": "^3.29.5",
+ "bs58": "^6.0.0",
+ "@solana/sysvars": "^3.0.3"
+ },
+ "peerDependencyRules": {
+ "allowedVersions": {
+ "@stellar/stellar-sdk": "14",
+ "rollup": "3",
+ "react": "18",
+ "@types/react": "18"
+ },
+ "ignoreMissing": [
+ "react-native"
+ ]
+ }
+ },
"packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402"
}
diff --git a/packages/connectkit/PAYMENT_FLOW.md b/packages/connectkit/PAYMENT_FLOW.md
new file mode 100644
index 000000000..98337d33f
--- /dev/null
+++ b/packages/connectkit/PAYMENT_FLOW.md
@@ -0,0 +1,355 @@
+# Payment Flow Documentation
+
+## Overview
+
+This document describes the payment flow architecture in the ConnectKit payment system, including state management, payment methods, and cross-chain payment handling.
+
+---
+
+## Payment States
+
+The system manages payment state through a centralized state machine:
+
+```mermaid
+stateDiagram-v2
+ [*] --> preview: createPreviewOrder()
+ preview --> payment_unpaid: setPaymentUnpaid()
+ payment_unpaid --> payment_started: setPaymentStarted()
+ payment_started --> payment_completed: setPaymentCompleted()
+ payment_started --> error: Payment fails
+ payment_started --> payment_unpaid: Cancel/Reset
+ error --> payment_unpaid: Retry
+ payment_completed --> [*]
+
+ note right of preview
+ Order created but not initiated
+ User can modify parameters
+ end note
+
+ note right of payment_unpaid
+ Payment ID assigned
+ Ready to start payment
+ end note
+
+ note right of payment_started
+ Payment in progress
+ Transaction being processed
+ end note
+```
+
+---
+
+## Main Payment Flow
+
+### 1. Initial Setup
+
+```mermaid
+flowchart TD
+ Start([User Initiates Payment]) --> CheckParams{Valid PayParams?}
+ CheckParams -->|No| Error[Show Error]
+ CheckParams -->|Yes| CreatePreview[createPreviewOrder]
+ CreatePreview --> RouteSelect[Route to SELECT_METHOD]
+ RouteSelect --> SelectMethod[User Selects Payment Method]
+
+ SelectMethod --> CheckChain{Payment Chain?}
+ CheckChain -->|EVM Chain| TokenFlow[Token Payment Flow]
+ CheckChain -->|Stellar| StellarFlow[Stellar Payment Flow]
+ CheckChain -->|Solana| SolanaFlow[Solana Payment Flow]
+```
+
+### 2. Token Payment Flow (EVM Chains)
+
+```mermaid
+flowchart TD
+ Start([PayWithToken Component]) --> SelectToken[User Selects Token Option]
+ SelectToken --> CheckCrossChain{Cross-chain
payment?}
+
+ CheckCrossChain -->|No| DirectPayment[Direct Payment]
+ CheckCrossChain -->|Yes| CreateRozoPayment[Create Rozo Payment]
+
+ CreateRozoPayment --> HydrateOrder[Hydrate Order]
+ DirectPayment --> HydrateOrder
+
+ HydrateOrder --> StateTransition{Current State?}
+ StateTransition -->|preview| SetUnpaid[setPaymentUnpaid]
+ StateTransition -->|payment_unpaid| SetStarted[setPaymentStarted]
+ StateTransition -->|payment_started| CheckSwitch{Chain switch?}
+
+ CheckSwitch -->|Yes| ResetOld[setPaymentUnpaid old]
+ CheckSwitch -->|No| SetStarted
+ ResetOld --> SetStarted
+
+ SetUnpaid --> SetStarted
+ SetStarted --> WaitWallet[Wait for Wallet Confirmation]
+ WaitWallet --> Complete[setPaymentCompleted]
+ Complete --> ConfirmRoute[Route to CONFIRMATION]
+```
+
+### 3. Stellar Payment Flow
+
+```mermaid
+flowchart TD
+ Start([PayWithStellarToken Component]) --> ValidateParams{Valid payParams?}
+ ValidateParams -->|No| Skip[Skip transfer - stale state]
+ ValidateParams -->|Yes| ValidateDest{Valid destination
address?}
+
+ ValidateDest -->|No| ErrorMsg[Throw Error: Address Required]
+ ValidateDest -->|Yes| CheckOrder{Order
initialized?}
+
+ CheckOrder -->|No| ErrorMsg2[Throw Error: Order Not Init]
+ CheckOrder -->|Yes| CheckCrossChain{Cross-chain
payment?}
+
+ CheckCrossChain -->|Yes| CreatePayment[createPayment]
+ CheckCrossChain -->|No| CheckState{Current
State?}
+
+ CreatePayment --> FormatOrder[formatPaymentResponseToHydratedOrder]
+ CheckState -->|payment_unpaid or
payment_started| UseExisting[Use Existing Order]
+ CheckState -->|Other| HydrateExisting[hydrateOrder]
+
+ FormatOrder --> ValidateHydrated{Hydrated
Order Valid?}
+ UseExisting --> ValidateHydrated
+ HydrateExisting --> ValidateHydrated
+
+ ValidateHydrated -->|No| ErrorMsg3[Throw Error: Payment Not Found]
+ ValidateHydrated -->|Yes| SetPaymentId[setRozoPaymentId]
+
+ SetPaymentId --> StateCheck{Check Store State}
+ StateCheck -->|payment_started
+ new payment| TransitionUnpaid[setPaymentUnpaid old]
+ StateCheck -->|preview| PreviewToUnpaid[setPaymentUnpaid → setPaymentStarted]
+ StateCheck -->|payment_unpaid| DirectStart[setPaymentStarted]
+ StateCheck -->|Already started| Continue[Continue]
+
+ TransitionUnpaid --> StartNew[setPaymentStarted new]
+ PreviewToUnpaid --> RequestPayment
+ DirectStart --> RequestPayment
+ StartNew --> RequestPayment
+ Continue --> RequestPayment
+
+ RequestPayment[Request Payment: payWithStellarToken] --> SignTx[Sign Transaction]
+ SignTx --> WaitConfirm[Wait for User Confirmation]
+ WaitConfirm --> Submit[Submit to Stellar Network]
+ Submit --> CheckSuccess{Transaction
Successful?}
+
+ CheckSuccess -->|Yes| SetCompleted[setPaymentCompleted]
+ CheckSuccess -->|No| HandleError[Handle Error]
+
+ HandleError --> CheckOrderExists{Order & Payment ID
exist?}
+ CheckOrderExists -->|Yes| ResetToUnpaid[setPaymentUnpaid with order]
+ CheckOrderExists -->|No| LogError[Log: Cannot set unpaid]
+
+ ResetToUnpaid --> CheckRejected{User
Rejected?}
+ LogError --> CheckRejected
+ CheckRejected -->|Yes| StateCancelled[State: RequestCancelled]
+ CheckRejected -->|No| StateFailed[State: RequestFailed]
+
+ SetCompleted --> RouteConfirm[Route to CONFIRMATION]
+```
+
+### 4. Solana Payment Flow
+
+The Solana payment flow is identical to the Stellar flow, with these differences:
+- Uses `PayWithSolanaToken` component
+- Uses `payWithSolanaToken` instead of `payWithStellarToken`
+- Validates Solana addresses instead of Stellar addresses
+- Submits to Solana network instead of Stellar
+
+---
+
+## Reset Payment Flow
+
+```mermaid
+flowchart TD
+ Start([resetPayment Called]) --> MergeParams[Merge New PayParams with Current]
+ MergeParams --> ClearState[Clear Old State]
+
+ ClearState --> ClearOptions[setSelectedStellarTokenOption undefined
setSelectedSolanaTokenOption undefined
setSelectedTokenOption undefined]
+ ClearOptions --> ClearWallet[setSelectedWallet undefined
setSelectedWalletDeepLink undefined]
+ ClearWallet --> ResetPay[pay.reset]
+
+ ResetPay --> CheckNewParams{New PayParams
Provided?}
+ CheckNewParams -->|No| RouteSelect[Route to SELECT_METHOD]
+ CheckNewParams -->|Yes| ConvertSymbols[Convert preferredSymbol to preferredTokens]
+
+ ConvertSymbols --> CheckChain{Destination
Chain?}
+ CheckChain -->|Stellar| SetStellarAddr[toStellarAddress = toAddress
toAddress = 0x0
toSolanaAddress = undefined]
+ CheckChain -->|Solana| SetSolanaAddr[toSolanaAddress = toAddress
toAddress = 0x0
toStellarAddress = undefined]
+ CheckChain -->|EVM| ClearSpecial[toStellarAddress = undefined
toSolanaAddress = undefined]
+
+ SetStellarAddr --> CreatePreview[createPreviewOrder]
+ SetSolanaAddr --> CreatePreview
+ ClearSpecial --> CreatePreview
+
+ CreatePreview --> UpdateParams[setCurrPayParams]
+ UpdateParams --> RouteSelect
+```
+
+---
+
+## State Transition Rules
+
+### From Preview State
+- Can transition to: `payment_unpaid`
+- Requires: Order data
+- Action: `setPaymentUnpaid(paymentId, order)`
+
+### From Payment Unpaid State
+- Can transition to: `payment_started`
+- Requires: Payment ID and order
+- Action: `setPaymentStarted(paymentId, hydratedOrder)`
+
+### From Payment Started State
+- Can transition to:
+ - `payment_completed` (success)
+ - `payment_unpaid` (cancel/reset)
+ - `error` (failure)
+- Special case: Cross-chain switch requires transition to `payment_unpaid` first
+
+### From Error State
+- Cannot call `setPaymentUnpaid` without providing order
+- Must provide both `paymentId` and `order` parameters
+
+---
+
+## Cross-Chain Payment Handling
+
+```mermaid
+flowchart TD
+ Start([User Selects Token]) --> CheckPreferred{Order has
preferredChainId?}
+ CheckPreferred -->|No| DirectPayment[Direct Payment on Selected Chain]
+ CheckPreferred -->|Yes| CheckMatch{preferredChainId ==
selectedToken.chainId?}
+
+ CheckMatch -->|Yes| DirectPayment
+ CheckMatch -->|No| CrossChain[Cross-Chain Payment Required]
+
+ CrossChain --> CreateRozo[createPayment via Rozo]
+ CreateRozo --> GetNewId[Get New Payment ID]
+ GetNewId --> CheckCurrentState{Current State?}
+
+ CheckCurrentState -->|payment_started| HandleSwitch[Handle Chain Switch]
+ CheckCurrentState -->|Other| NormalFlow[Normal Flow]
+
+ HandleSwitch --> UnpaidOld[setPaymentUnpaid old payment]
+ UnpaidOld --> StartNew[setPaymentStarted new payment]
+
+ NormalFlow --> StartNew
+ DirectPayment --> UseExisting[Use Existing Order]
+```
+
+---
+
+## Key Components
+
+### usePaymentState Hook
+- **Location**: `packages/connectkit/src/hooks/usePaymentState.ts`
+- **Responsibilities**:
+ - Manages `currPayParams` state
+ - Handles `resetOrder` logic
+ - Clears selected options on reset
+ - Routes to appropriate payment flow
+
+### useStellarDestination Hook
+- **Location**: `packages/connectkit/src/hooks/useStellarDestination.ts`
+- **Responsibilities**:
+ - Derives destination address from `payParams`
+ - Determines payment direction (Stellar → Base, Base → Stellar, etc.)
+ - Returns memoized values based on `payParams`
+
+### Payment Components
+1. **PayWithToken** - EVM chain payments
+2. **PayWithStellarToken** - Stellar network payments
+3. **PayWithSolanaToken** - Solana network payments
+
+---
+
+## Common Issues & Solutions
+
+### Issue: Stale Destination Address After Reset
+
+**Symptom**: After `resetPayment`, component uses old destination address from previous payment attempt.
+
+**Root Cause**:
+- `destinationAddress` from `useStellarDestination(payParams)` is memoized
+- React batches state updates, so component may not re-render with new `payParams` before `useEffect` triggers
+
+**Solution**:
+1. Validate `payParams` exists before processing transfer
+2. Add logging to track destination address and chain info
+3. Check for stale state and skip processing if detected
+
+```typescript
+// Validate we have current payParams - if not, component has stale state
+if (!payParams) {
+ log?.("[Component] No payParams available, skipping transfer");
+ setIsLoading(false);
+ return;
+}
+```
+
+### Issue: setPaymentUnpaid Error When State is "error"
+
+**Symptom**: `Error: Cannot set payment unpaid: Order must be provided when state is error`
+
+**Root Cause**:
+- Error handler calls `setPaymentUnpaid(paymentId)` without order parameter
+- When state is "error", order must be provided
+
+**Solution**:
+```typescript
+if (rozoPaymentId && order && 'org' in order) {
+ try {
+ await setPaymentUnpaid(rozoPaymentId, order as any);
+ } catch (e) {
+ console.error("Failed to set payment unpaid:", e);
+ }
+} else {
+ log?.(`Cannot set payment unpaid - missing requirements`);
+}
+```
+
+---
+
+## Payment State Validation
+
+### Required Checks Before Payment
+1. ✅ `payParams` exists and is current
+2. ✅ `destinationAddress` is valid for target chain
+3. ✅ `order` is initialized
+4. ✅ Selected token option matches payment parameters
+5. ✅ Wallet is connected (for blockchain payments)
+
+### State Transition Validation
+1. ✅ Cannot go from `error` to `payment_unpaid` without order
+2. ✅ Cannot go from `preview` to `payment_started` without going through `payment_unpaid`
+3. ✅ Cross-chain switch requires resetting old payment before starting new one
+
+---
+
+## Debugging Tips
+
+### Enable Logging
+Look for log statements in the components:
+```typescript
+log?.(`[PayWithStellarToken] Payment setup - destAddress: ${finalDestAddress}, toChain: ${payParams.toChain}, token chain: ${option.required.token.chainId}`);
+```
+
+### Check State Transitions
+Monitor the payment state in Redux DevTools or logs:
+```typescript
+const currentState = store.getState().type;
+console.log('Current payment state:', currentState);
+```
+
+### Validate payParams
+Check if `payParams` are being updated correctly:
+```typescript
+console.log('payParams:', JSON.stringify(payParams, null, 2));
+```
+
+### Check Destination Address
+Verify destination address derivation:
+```typescript
+const { destinationAddress } = useStellarDestination(payParams);
+console.log('Destination:', destinationAddress);
+console.log('ToChain:', payParams?.toChain);
+console.log('ToStellarAddress:', payParams?.toStellarAddress);
+```
diff --git a/packages/connectkit/bundle-analysis.html b/packages/connectkit/bundle-analysis.html
index be33d2e77..759f31475 100644
--- a/packages/connectkit/bundle-analysis.html
+++ b/packages/connectkit/bundle-analysis.html
@@ -4929,7 +4929,7 @@