Two is a B2B payment method that lets your business customers pay by invoice with instant credit decisioning. This module integrates Two with PrestaShop 1.7.6+ following PrestaShop best practices and security standards.
- Two Payment Option: Visible for business accounts at checkout
- Company Search: Real-time company search and selection against Two's Company API v2
- Organization Number Capture: Hidden field (
companyid) automatically populated from company selection - Order Intent Check: Frontend validation before payment confirmation
- Server-Side Verification: Defense-in-depth security with server-side Order Intent verification
- Payment Terms UI: Configurable payment terms (7/15/20/30/45/60/90 days) with user selection
- Admin Integration: Two order ID, state, status, and invoice URL displayed in order pages
- Invoice Upload: Automatic upload of PrestaShop-generated invoices to Two (optional feature)
- SSL certificate verification enabled by default
- Server-side and client-side Order Intent validation
- Rate limiting on Order Intent API calls
- Secure API key storage and validation
- Cross-version compatibility (PrestaShop 1.7.6 - 9.x)
- Theme-agnostic implementation
- jQuery compatibility handling for older PrestaShop versions
- Comprehensive error logging
- Order payload validation ensuring exact PrestaShop invoice matching
- PrestaShop: 1.7.6+ (tested up to 9.x)
- PHP: 7.2+
- Theme: Classic theme or 1.7-compatible themes
- B2B Support: Store must support B2B (business account type at address step)
- SSL: HTTPS required for production
- Download the module package
- Go to PrestaShop Admin → Modules → Module Manager
- Click "Upload a module" and select the module ZIP file
- Click "Install" after upload completes
- Configure the module (see Configuration section)
- Install and Enable: Install the module from Module Manager
- Environment Selection: Choose Sandbox (testing) or Production environment
- API Key: Enter your Two API key for the selected environment
- The module validates the API key on save
- Invalid keys will show an error message
- Payment Terms: Configure available payment terms
- Enable/disable individual terms: 7, 15, 20, 30, 45, 60, 90 days
- Set default payment term (defaults to 30 days if available)
- Optional Features:
- Enable/disable company name field requirement
- Enable/disable organization number field requirement
- Enable/disable department field
- Enable/disable project field
- Enable/disable Order Intent check (Required for use)
- Enable/disable account type selection
- Enable automatic invoice upload to Two
- Configure SSL verification (default: enabled)
| Option | Description | Default |
|---|---|---|
| Environment | Sandbox or Production | Sandbox |
| API Key | Two merchant API key | Required |
| Payment Terms | Available terms (7-90 days) | 30 days enabled |
| Default Payment Term | Default term when multiple available | 30 days |
| Company Name | Require company name field | Enabled |
| Organization Number | Require organization number | Enabled |
| Department Field | Show department field | Disabled |
| Project Field | Show project field | Disabled |
| Order Intent | Enable Order Intent check | Enabled |
| Account Type | Show account type selector | Enabled |
| Auto Fulfill Orders | Automatically fulfill orders with Two when status changes | Enabled |
| Invoice Upload | Auto-upload invoices to Two | Disabled |
| SSL Verification | Verify SSL certificates | Enabled |
- Customer types at least 3 characters in the
Companyfield - Module searches Two's Company API v2 (frontend call)
- Customer selects a company from search results
- Module stores organization number in hidden
companyidfield - Address fields auto-fill when available from Two's data
- Selection persists in cookie to survive checkout step changes
- Two payment option appears for business accounts
- When selected, module runs Order Intent check (frontend)
- If Approved:
- Success message displayed
- Payment terms selector shown
- Customer can select payment term (e.g., 30 days)
- Order can proceed
- If Declined:
- Error message displayed
- Payment method blocked
- Customer must choose alternative payment
- Customer clicks "Place Order" with Two selected
- Module verifies Order Intent server-side (defense-in-depth)
- If valid, PrestaShop order created
- Two order created via API
- Payment data saved to database
- Customer redirected to confirmation page
How It Works:
- Fulfillment is triggered automatically when you change the PrestaShop order status to one of your configured fulfillment statuses (default: "Shipped")
- The module calls Two's fulfillment API endpoint (
/v1/order/{id}/fulfillments) - This marks the entire order as fulfilled in Two's system
- Buyer payment terms become active and the payout cycle begins
- Order state changes from
CONFIRMEDtoFULFILLEDin Two
Configuration:
- Configure fulfillment trigger statuses in module settings: Two → Configuration → Order Status Mapping → Fulfillment Statuses
- You can select multiple statuses (hold Ctrl/Cmd to select multiple)
- Default: "Shipped" status triggers fulfillment
- The form field shows currently active statuses in green text for easy reference
- After saving, a confirmation message displays all active fulfillment trigger statuses
- Ensure "Automatically fulfill orders with Two" option is enabled (default: enabled)
- The Two PrestaShop plugin only supports complete fulfillment of the entire PrestaShop order
- Partial fulfillments/captures are NOT supported from PrestaShop
- When you change the order status to a fulfillment trigger status, the entire order is marked as fulfilled in Two
- If you need to fulfill orders partially, you must:
- Process partial fulfillment manually in PrestaShop (split orders, etc.)
- Then use Two's Merchant Portal to handle partial fulfillments directly
Invoice Upload (Optional):
- If "Use Own Invoices" is enabled in module configuration:
- After successful fulfillment, the module automatically generates the PrestaShop invoice PDF
- Uploads it to Two via a three-step process:
- Request signed upload URL from Two API
- Upload PDF to Google Cloud Storage using the signed URL
- Poll upload status until Two validates the invoice
- Upload status is tracked in order metadata (
two_invoice_upload_status) - Upload statuses:
PENDING,UPLOADING,UPLOADED,FAILED,NOT_APPLICABLE - Check PrestaShop logs for upload progress and any errors
Fulfillment Requirements:
- Order must be in
CONFIRMEDstate in Two (notVERIFIED,CANCELLED, orREFUNDED) - Orders in
VERIFIEDstate will be skipped - they must be confirmed first - Order must have a valid Two order ID stored in PrestaShop
- Module must have valid API credentials configured
Troubleshooting Fulfillment:
- Check PrestaShop logs for fulfillment errors (search for "TwoPayment: Fulfillment")
- Verify order is in
CONFIRMEDstate before fulfillment (notVERIFIED) - If order is
VERIFIED, it must be confirmed first (either manually in Two Merchant Portal or via Two's confirmation flow) - Ensure "Automatically fulfill orders with Two" is enabled in module settings
- Verify fulfillment status is correctly mapped in module configuration
- Check the Order Status Mapping form to see which statuses are currently active (shown in green)
How It Works:
- Full refunds are supported automatically via PrestaShop order status change
- When you change the order status to your configured refund status (default: "Refunded"), the module:
- Calls Two's refund API endpoint (
/v1/order/{id}/refund) - Issues a full refund for the entire order amount
- Two immediately issues a credit note to the buyer
- Order state changes to
REFUNDEDin Two - Idempotency keys prevent duplicate refund calls (race condition protection)
- Calls Two's refund API endpoint (
Configuration:
- Configure refund trigger status in module settings: Two → Configuration → Order Status Mapping → Two: Order Refunded
- Default: "Refunded" status triggers full refund
- The module checks if order is already refunded to prevent duplicate refunds
- The Two PrestaShop plugin only supports full refunds via PrestaShop order status changes
- Partial refunds are NOT supported from PrestaShop
- When you change the order status to the refund trigger status, the entire order is refunded in Two
Partial Refunds - Manual Process Required: If you need to issue a partial refund:
-
Process partial refund in PrestaShop (as you normally would):
- Go to the order in PrestaShop admin
- Issue partial refund through PrestaShop's refund interface
- This updates PrestaShop's order records
-
Process partial refund in Two Merchant Portal:
- Log into your Two Merchant Portal
- Find the corresponding Two order
- Issue the partial refund manually through Two's interface
- This step is REQUIRED - the partial refund will NOT be reflected in Two's system if you only process it in PrestaShop
-
Why manual process is required:
- Two's API requires specific refund amounts and reasons for partial refunds
- PrestaShop's partial refund interface doesn't provide the necessary details to Two's API
- Manual processing ensures accurate refund amounts and proper credit note generation
- Failing to process partial refunds in both systems will result in:
- Partial refund existing only in PrestaShop
- Full order amount still owed in Two's system
- Buyer will be charged for the full amount despite partial refund
- Accounting discrepancies between systems
- Potential customer service issues
Refund Requirements:
- Order must be in
FULFILLEDstate in Two (cannot refund unfulfilled orders) - Order must have a valid Two order ID stored in PrestaShop
- Module must have valid API credentials configured
- Order must not already be fully refunded (module checks this automatically)
Troubleshooting Refunds:
- Check PrestaShop logs for refund errors (search for "TwoPayment: Refund")
- Verify order is in
FULFILLEDstate before attempting refund - Common errors:
- HTTP 400: Order not in refundable state (must be
FULFILLED) - HTTP 409: Duplicate refund attempt (already refunded)
- HTTP 500: Two API temporarily unavailable (retry later)
- HTTP 400: Order not in refundable state (must be
twopayment/
├── twopayment.php # Main module class
├── config.xml # Module metadata
├── classes/
│ └── TwoInvoiceUploadService.php # Invoice upload service
├── controllers/
│ └── front/
│ ├── payment.php # Payment processing
│ ├── confirmation.php # Order confirmation
│ ├── cancel.php # Order cancellation
│ └── orderintent.php # Order Intent AJAX
├── views/
│ ├── css/
│ │ └── two.css # Module styles
│ ├── js/
│ │ ├── twopayment.js # Main JS
│ │ └── modules/ # Modular JS components
│ └── templates/
│ └── hook/
│ └── paymentinfo.tpl # Payment UI template
└── upgrade/ # Version upgrade scripts
- Twopayment: Main module class handling hooks, configuration, API calls
- TwoInvoiceUploadService: Handles invoice PDF upload to Two
- TwoCheckoutManager: Frontend checkout flow management
- TwoOrderIntent: Order Intent validation (client-side)
- TwoCompanySearch: Company search functionality
/v1/merchant/verify_api_key- API key validation/v1/order/intent- Order Intent check/v1/order- Order creation/v1/order/{id}- Order updates, refunds/v1/invoice/{id}/upload- Invoice upload initiation/companies/v2/company- Company search
The module builds order payloads that exactly match PrestaShop invoices:
- Line items with exact net, tax, and gross amounts
- Tax subtotals matching PrestaShop calculations
- Product names including attributes (e.g., "Shirt (Size: S - Color: White)")
- Shipping and discount line items
- Buyer and shipping addresses
- Payment terms
- Symptom: No results when typing company name
- Solutions:
- Ensure at least 3 characters typed
- Check browser console for API errors
- Verify network calls to
/companies/v2/company - Check API key is valid
- Verify country selection (if applicable)
- Symptom: Order Intent check doesn't run when Two selected
- Solutions:
- Verify Two payment radio button is selected
- Check browser console for JavaScript errors
- Ensure payment section isn't re-rendered by theme without events
- Module listens to PrestaShop
updatedPaymentFormevent - Check PrestaShop logs for server-side errors
- Symptom: Order creation fails with error message
- Solutions:
- Check PrestaShop logs for server-side Order Intent errors
- Verify company data persisted (cookie and hidden
companyidfield) - Ensure Order Intent was approved (check cookie
two_order_intent_approved) - Verify API key is correct for environment
- Check Two API status
- Symptom: Invoices not uploading to Two
- Solutions:
- Verify "Use Own Invoices" option enabled in module config
- Check PrestaShop logs for upload errors
- Verify PDF generation works (test invoice download)
- Check file size limits (max 2MB)
- Verify SSL certificate issues (if corporate network)
- Symptom: Payment terms selector not visible
- Solutions:
- Verify at least one payment term enabled in config
- Check Order Intent was approved
- Verify JavaScript loaded correctly
- Check browser console for errors
- ✅ SSL certificate verification (configurable override for corporate networks)
- ✅ SQL injection prevention using PrestaShop standards
- ✅ Input validation and sanitization
- ✅ Server-side Order Intent verification
- ✅ Rate limiting on API calls
- ✅ Secure API key storage
- ✅ CSRF token validation for AJAX requests
- Always use HTTPS in production
- Keep module updated to latest version
- Regularly rotate API keys
- Monitor PrestaShop logs for suspicious activity
- Use strong API keys (provided by Two)
- Module documentation: See this README
- Two API documentation: Contact Two support
- PrestaShop documentation: https://devdocs.prestashop.com/
- Technical Issues: Check PrestaShop logs (
var/logs/) - API Issues: Check Two API status and logs
- Module Bugs: Contact Two support at support@two.inc
- Onboarding: Contact Two support for production enablement
Module logs to PrestaShop's standard logging system:
- Location:
var/logs/[date].log - Levels: Info (1), Warning (2), Error (3), Major (4)
- Search: Look for "TwoPayment" prefix
See CHANGELOG.md for detailed version history and changes.
Two Commercial License
© 2021-2025 Two Team