Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
976ab69
Create onboarding flow techincal document
Dinamiko Sep 18, 2025
bda5742
Add post-onboarding document
Dinamiko Sep 18, 2025
fd1d0e3
Add post-onboarding document
Dinamiko Sep 18, 2025
ccadf52
Add post-onboarding document
Dinamiko Sep 18, 2025
4b91065
Add merchant capabilitlities service
Dinamiko Sep 18, 2025
2f5dbf6
Add merchant capabilitlities service
Dinamiko Sep 18, 2025
81750fc
Remove redundant content
Dinamiko Sep 18, 2025
cf82350
Add capabilty checks for connected merchant into service wcgateway.fe…
Dinamiko Sep 19, 2025
317400f
Remove get_shop_country from MerchantDetails
Dinamiko Sep 19, 2025
887c3ca
Merge branch 'develop' into PCP-5311-documentation-task-vaulting
Dinamiko Sep 29, 2025
60eb84b
refactor: Centralize Feature eligibility in Merchant Details
puntope Sep 30, 2025
9a53273
refactor: Simplify features logic
puntope Oct 6, 2025
b310999
Restore currency check in advanced_credit_and_debit_cards
puntope Oct 7, 2025
174506b
Remove early return
puntope Oct 7, 2025
1c7f893
Fix pay-later key
puntope Oct 7, 2025
106b6ec
Merge branch 'develop' into PCP-5311-documentation-task-vaulting
puntope Oct 7, 2025
53f9cf2
Completely Remove installments if Merchant is not MX
puntope Oct 7, 2025
e328c5f
Remove docs
puntope Oct 7, 2025
7398a32
Fix potential issue with pay_later_messaging
puntope Oct 7, 2025
1662ba2
Rename variable
puntope Oct 9, 2025
cd3d1af
Rename variable
puntope Oct 9, 2025
13a0de3
PHPCS
puntope Oct 9, 2025
13dce9c
Restore paypal venmo check
puntope Oct 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions modules/ppcp-settings/docs/merchant-onboarding-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# Merchant Onboarding Flow

The onboarding flow is a multi-step wizard that guides merchants through setting up their PayPal integration. The flow is dynamic and adapts based on merchant preferences, store configuration, and country-specific capabilities determined by configuration flags.

## Core Architecture

### Configuration Flags Value Determination

The onboarding configuration flags are determined during the `OnboardingProfile` instantiation in the dependency injection container. Each flag represents a specific capability or business rule that affects the onboarding flow.

**File:** `modules/ppcp-settings/services.php`

```php
'settings.data.onboarding' => static function (ContainerInterface $container): OnboardingProfile {
$can_use_casual_selling = $container->get('settings.casual-selling.eligible');
$can_use_vaulting = $container->has('save-payment-methods.eligible') && $container->get('save-payment-methods.eligible');
$can_use_card_payments = $container->has('card-fields.eligible') && $container->get('card-fields.eligible');
$can_use_subscriptions = $container->has('wc-subscriptions.helper') && $container->get('wc-subscriptions.helper')->plugin_is_active();
$should_skip_payment_methods = class_exists('\WC_Payments');
$can_use_fastlane = $container->get('axo.eligible');
$can_use_pay_later = $container->get('button.helper.messages-apply');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Is it useful to repeat the full service code here? Looks like a lot of maintenance to keep this doc in-sync with the code + does not add a huge benefit to the documentation.

A permalink would be sufficient IMO (which is usually rendered as embed in GH)
File: modules/ppcp-settings/services.php

'settings.data.onboarding' => static function ( ContainerInterface $container ): OnboardingProfile {
$can_use_casual_selling = $container->get( 'settings.casual-selling.eligible' );
$can_use_vaulting = $container->has( 'save-payment-methods.eligible' ) && $container->get( 'save-payment-methods.eligible' );
$can_use_card_payments = $container->has( 'card-fields.eligible' ) && $container->get( 'card-fields.eligible' );
$can_use_subscriptions = $container->has( 'wc-subscriptions.helper' ) && $container->get( 'wc-subscriptions.helper' )
->plugin_is_active();
$should_skip_payment_methods = class_exists( '\WC_Payments' );
$can_use_fastlane = $container->get( 'axo.eligible' );
$can_use_pay_later = $container->get( 'button.helper.messages-apply' );
return new OnboardingProfile(
$can_use_casual_selling,
$can_use_vaulting,
$can_use_card_payments,
$can_use_subscriptions,
$should_skip_payment_methods,
$can_use_fastlane,
$can_use_pay_later->for_country()
);
},


return new OnboardingProfile(/* ...flags... */);
},
```

#### Flag Value Sources

**`can_use_casual_selling`** - Personal account support
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: To me this looks a bit like code documentation that should be present in the .php file.
Also, the service name is already visible in the code snippet above and describing the implementation logic here seems risky to get outdated as well.

I would keep this flag overview more concise, documenting the "business perspective", e.g.

  • can_use_casual_selling - Business accounts are supported in all countries; this flag indicates if the store country also supports onboarding using a personal account
  • can_use_vaulting - Whether the store's country supports vaulting - displays additional onboarding choices
  • should_skip_payment_methods - Whether the "branded only" mode is active and the onboarding wizard is reduced
  • ...

- **Source:** `settings.casual-selling.eligible` service (`services.php`)
- **Logic:** Checks if store's country is in supported countries list
- **Countries:** Countries including US, CA, AU, GB, EU countries, etc. (`services.php`)
- **Determination:** `in_array($country, $eligible_countries, true)`

**`can_use_vaulting`** - Saved payment methods capability
- **Source:** `save-payment-methods.eligible` service from SavePaymentMethods module
- **File:** `modules/ppcp-save-payment-methods/services.php`
- **Logic:** `SavePaymentMethodsApplies::for_country() && SavePaymentMethodsApplies::for_merchant()`
- **Countries:** Countries including major markets (AU, AT, BE, CA, DE, FR, etc.)
- **Merchant Check:** PayPal account must support vaulting capabilities

**`can_use_card_payments`** - Credit card processing via PayPal
- **Source:** `card-fields.eligible` service from CardFields module
- **File:** `modules/ppcp-card-fields/services.php`
- **Logic:** `CardFieldsApplies::for_country() && CardFieldsApplies::for_merchant()`
- **Countries:** Countries with credit card processing support
- **Merchant Check:** PayPal account must have ACDC (Advanced Credit and Debit Cards) capability

**`can_use_subscriptions`** - WooCommerce Subscriptions integration
- **Source:** `wc-subscriptions.helper` service availability check
- **Logic:** Checks if WooCommerce Subscriptions plugin is active
- **Determination:** `$container->get('wc-subscriptions.helper')->plugin_is_active()`

**`should_skip_payment_methods`** - Skip payment methods selection screen
- **Source:** Direct class existence check
- **Logic:** `class_exists('\WC_Payments')`
- **Purpose:** Skip payment methods screen when WooCommerce Payments plugin is active (avoid conflicts)

**`can_use_fastlane`** - Fastlane/AXO checkout capability
- **Source:** `axo.eligible` service from AXO module
- **Dependencies:** Requires ACDC capability and specific country/merchant eligibility
- **Purpose:** Determines if Advanced Checkout Options (one-click checkout) can be offered

**`can_use_pay_later`** - Pay Later messaging availability
- **Source:** `button.helper.messages-apply` service from Button module
- **File:** `modules/ppcp-button/services.php` (MessagesApply class)
- **Logic:** `MessagesApply::for_country()` - checks country against PayPal's Pay Later supported regions
- **Countries:** Pay Later messaging supported in US, AU, DE, FR, GB, IT, ES and other select markets

#### Country-Based Eligibility Services

Multiple modules implement country-based eligibility patterns:

```php
// Common pattern across modules
'module.helpers.applies' => function(ContainerInterface $container): ModuleApplies {
return new ModuleApplies(
$container->get('module.supported-countries'), // Country whitelist
$container->get('api.shop.country') // Current store country
);
},

// Eligibility check
'module.eligible' => function(ContainerInterface $container): bool {
$applies = $container->get('module.helpers.applies');
return $applies->for_country() && $applies->for_merchant();
},
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The whole "Country-Based Eligibility Services" section seems unrelated to the document topic - which should be about the "Onboarding Flow" - as it documents generic architecture instead of explaining onboarding details.

It's a nice idea to document code architecture, but let's move this to a different document. This section is confusing for new developers, especially since the code looks valid but actually displays pseudo-code (there is no service named module.helpers.applies, but specific services like applepay.helpers.apm-applies, which is not mentioned here)


#### Casual Selling Country List

The casual selling feature (personal PayPal accounts) supported countries are defined in: `modules/ppcp-settings/services.php`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Remove this, as code navigation within a single service file should be easy enough for every developer, and this description adds no value when reading the document in isolation. IMO this list/reference is also more related to a documentation about "Casual Seller vs Business Merchants"


### API Endpoint: `wc/v3/wc_paypal/onboarding`

The central REST API endpoint manages all onboarding state and configuration:

**File:** `modules/ppcp-settings/src/Endpoint/OnboardingRestEndpoint.php`

#### Endpoints:
- **GET** `/wp-json/wc/v3/wc_paypal/onboarding` - Retrieves current onboarding state and flags
- **POST** `/wp-json/wc/v3/wc_paypal/onboarding` - Updates onboarding progress and settings

#### Key Data Structures:

```php
// Onboarding state fields (OnboardingProfile)
'completed' => bool // Onboarding process completion status
'step' => int // Current step index (0-based)
'is_casual_seller' => bool // Personal vs business account selection
'accept_card_payments' => bool // Whether to enable card payment methods
'products' => array // Selected product types ['virtual', 'physical', 'subscriptions']
'gateways_synced' => bool // Payment gateway sync status
'gateways_refreshed' => bool // Payment gateway refresh status
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Could we add those code comments to the actual .php file, e.g. to the OnboardingProfile and drop a link here? Those comments are very helpful, but developers usually do not see them in this MD file

protected function get_defaults(): array {
return array(
'completed' => false,
'step' => 0,
'is_casual_seller' => null,
'accept_card_payments' => null,
'products' => array(),
'setup_done' => false,
'gateways_synced' => false,
'gateways_refreshed' => false,
);
}


// Configuration flags (read-only, determined by server)
'can_use_casual_selling' => bool // Country supports personal accounts
'can_use_vaulting' => bool // Vaulting/saved payment methods available
'can_use_card_payments' => bool // Credit card processing available
'can_use_subscriptions' => bool // WooCommerce Subscriptions detected
'should_skip_payment_methods' => bool // Skip payment methods selection screen
'can_use_fastlane' => bool // Fastlane checkout available
'can_use_pay_later' => bool // Pay Later options available
```

### Data Layer - Redux Store

**Store Name:** `wc/paypal/onboarding`
**Files:**
- `modules/ppcp-settings/resources/js/data/onboarding/constants.js` - Store configuration
- `modules/ppcp-settings/resources/js/data/onboarding/hooks.js` - React hooks for data access

The frontend uses WordPress Data API (Redux-based) to manage state:

```javascript
// Key hooks for accessing onboarding data
OnboardingHooks.useSteps() // Current step, completion status, flags
OnboardingHooks.useBusiness() // Casual seller selection
OnboardingHooks.useProducts() // Product types selection
OnboardingHooks.useOptionalPaymentMethods() // Payment method preferences
OnboardingHooks.useFlags() // Configuration flags from server
```

## Step Flow Control

### Step Definition and Filtering

**File:** `modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/index.js`

The onboarding consists of up to 5 steps that are dynamically filtered based on configuration flags:

```javascript
const ALL_STEPS = [
{ id: 'welcome', ... }, // Always shown
{ id: 'business', ... }, // Shown if canUseCasualSelling = true
{ id: 'products', ... }, // Always shown
{ id: 'methods', ... }, // Conditionally shown (see below)
{ id: 'complete', ... } // Always shown
];
```

### Dynamic Step Filtering Logic

Steps are filtered using the `getSteps(flags)` function based on:

1. **Business Step Filtering:**
```javascript
// Only show business step if casual selling is available in merchant's country
flags.canUseCasualSelling || step.id !== 'business'
```

2. **Payment Methods Step Filtering:**
```javascript
// Skip payment methods screen if:
const isBrandedBCDC = ownBrandOnly && !flags.canUseCardPayments;
const shouldSkip = flags.shouldSkipPaymentMethods ||
isCasualSeller ||
isBrandedBCDC;
```

**Conditions for skipping payment methods step:**
- `flags.shouldSkipPaymentMethods = true` (server-side business logic)
- Merchant selected casual seller account
- Store uses PayPal-only branding AND card payments unavailable

## Component Visibility Control

### Flag-Based Component Rendering

Components throughout the onboarding flow check flags to determine visibility:

**Example from Step Components:**
```javascript
// Business step - only renders if casual selling available
const { flags } = OnboardingHooks.useFlags();
if (!flags.canUseCasualSelling) {
return null; // Component not rendered
}
```

### Screen Navigation Logic

**File:** `modules/ppcp-settings/resources/js/Components/Screens/Onboarding/Steps/index.js`

```javascript
export const getCurrentStep = (requestedStep, steps) => {
// Ensures requested step exists in filtered steps array
const safeCurrentStep = isValidStep(requestedStep) ? requestedStep : 0;
return steps[safeCurrentStep];
};
```

Navigation between steps validates:
- Step index is within bounds of filtered steps
- `canProceed()` function returns true for current step
- Required data is collected before advancing

### Initial State Hydration and State Persistence

When the onboarding screen loads:

1. **Frontend** makes GET request to `/wc/v3/wc_paypal/onboarding`
2. **Backend** returns current state + flags via `OnboardingRestEndpoint::get_details()`
3. **Frontend** initializes Redux store with server data
4. **Components** render based on flags and current step

User interactions trigger:

1. **Frontend** updates local Redux state
2. **Frontend** makes POST request to `/wc/v3/wc_paypal/onboarding` with changes
3. **Backend** validates and persists via `OnboardingProfile::save()`
4. **Backend** returns updated state (including any derived flag changes)

## Key Integration Points

### Gateway Synchronization

**Files:**
- `modules/ppcp-settings/src/Data/OnboardingProfile.php`
- WordPress action: `woocommerce_paypal_payments_sync_gateways`

```php
public function set_gateways_synced(bool $synced): void {
$this->data['gateways_synced'] = $synced;
if ($synced) {
do_action('woocommerce_paypal_payments_sync_gateways');
}
}
```

### Connection Flow Integration

The onboarding integrates with PayPal's OAuth connection flow:
- Final step provides connection buttons/forms
- Connection success updates onboarding completion status
- Failed connections allow retry without losing progress

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add a link to the authentication-flows.md document

### Settings Application

Upon onboarding completion, the collected preferences are applied:
- Payment method enablement based on `accept_card_payments`
- Account type configuration based on `is_casual_seller`
- Product-specific features based on `products` array
Loading
Loading