feat: implement payment step
This commit is contained in:
@@ -0,0 +1,726 @@
|
||||
# Booking Payment Step Implementation Plan
|
||||
|
||||
**Status:** ✅ COMPLETED
|
||||
**Last Updated:** 2025-01-04
|
||||
**Goal:** Add payment method selection step (Step 3) to booking creation flow
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
✅ **Completed Components:**
|
||||
|
||||
1. **DTOs Created:**
|
||||
- `BankAccountDto` - IBAN validation, account holder, bank name, SEPA mandate
|
||||
- Payment properties added directly to `BookingCreateDto`:
|
||||
- `paymentMethod` - PAYMENT_METHOD_TRANSFER (default) or PAYMENT_METHOD_DEBIT
|
||||
- `bankAccount` - BankAccountDto instance (nullable)
|
||||
- Payment method constants in `Constants.php` (PAYMENT_METHOD_TRANSFER, PAYMENT_METHOD_DEBIT)
|
||||
|
||||
2. **Form Type:**
|
||||
- `BookingCreateStep3Type` - Main step 3 form with payment method and conditional bank account fields
|
||||
- **Removed** `PaymentType` wrapper (unnecessary with proper event handling)
|
||||
- Dynamic field management using POST_SET_DATA and PRE_SUBMIT events
|
||||
- Helper method `addBankAccountField()` to avoid duplication
|
||||
|
||||
3. **Controller:**
|
||||
- `CreateStep3Controller` - Main step 3 action and HTMX refresh endpoint
|
||||
- Uses `getSummaryVariables()` trait method for booking summary data
|
||||
- Conditional field rendering handled entirely by form events
|
||||
|
||||
4. **Template:**
|
||||
- `create_step_3.html.twig` - Grid layout matching steps 1 & 2
|
||||
- Payment method selection with HTMX for dynamic bank account fields
|
||||
- HTMX attributes on outer wrapper div for proper field replacement
|
||||
- Booking summary sidebar visible on all steps
|
||||
|
||||
5. **Validation:**
|
||||
- Conditional validation via `validateBankAccount()` callback in `BookingCreateDto`
|
||||
- Bank account fields only validated when payment method is DEBIT
|
||||
- IBAN format validation using Symfony's built-in `@Assert\Iban`
|
||||
- SEPA mandate acceptance required for direct debit
|
||||
|
||||
6. **Business Rules:**
|
||||
- **14-day rule**: Direct debit only available if travel starts ≥14 days from now
|
||||
- Debit option becomes readonly with tooltip when not available
|
||||
- Authorization text from existing booking flow integrated into choice label
|
||||
|
||||
## Key Implementation Details
|
||||
|
||||
### Form Event Pattern
|
||||
```php
|
||||
// BookingCreateStep3Type.php - Conditional field rendering
|
||||
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event): void {
|
||||
$bookingDto = $event->getData();
|
||||
|
||||
if (Constants::PAYMENT_METHOD_DEBIT === $bookingDto->paymentMethod) {
|
||||
$this->addBankAccountField($event->getForm());
|
||||
}
|
||||
});
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void {
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
$paymentMethod = $data['paymentMethod'] ?? null;
|
||||
|
||||
if ($form->has('bankAccount')) {
|
||||
$form->remove('bankAccount');
|
||||
}
|
||||
|
||||
if (Constants::PAYMENT_METHOD_DEBIT === $paymentMethod) {
|
||||
$this->addBankAccountField($form);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- POST_SET_DATA: Adds field when rendering if paymentMethod is DEBIT
|
||||
- PRE_SUBMIT: Removes existing field first, then adds only if DEBIT selected
|
||||
- Helper method avoids duplicate field configuration
|
||||
- No need to manually unset data - Symfony ignores unmapped fields
|
||||
|
||||
### Conditional Validation Pattern
|
||||
```php
|
||||
// BookingCreateDto.php - Conditional bank account validation
|
||||
#[Assert\Callback]
|
||||
public function validateBankAccount(ExecutionContextInterface $context): void
|
||||
{
|
||||
if (Constants::PAYMENT_METHOD_DEBIT !== $this->paymentMethod) {
|
||||
return; // Skip validation for transfer
|
||||
}
|
||||
|
||||
// Validate only when debit is selected
|
||||
if (null === $this->bankAccount) {
|
||||
$context->buildViolation('Bitte geben Sie Ihre Bankverbindung an.')
|
||||
->atPath('bankAccount')
|
||||
->addViolation();
|
||||
}
|
||||
// ... additional field validations
|
||||
}
|
||||
```
|
||||
|
||||
### 14-Day Availability Rule
|
||||
```php
|
||||
private function isDebitAvailable(BookingCreateDto $bookingDto): bool
|
||||
{
|
||||
$travelStartDate = CarbonImmutable::instance($bookingDto->travel->dateFrom);
|
||||
$now = CarbonImmutable::now();
|
||||
$daysUntilTravel = $now->diffInDays($travelStartDate, false);
|
||||
|
||||
return $daysUntilTravel >= 14;
|
||||
}
|
||||
```
|
||||
|
||||
Applied via `choice_attr` callback to make debit option readonly when unavailable.
|
||||
|
||||
### HTMX Pattern for Expanded Forms
|
||||
For radio buttons (expanded choice types), HTMX attributes must be on a wrapper element:
|
||||
|
||||
```twig
|
||||
<div id="form-payment"
|
||||
hx-post="{{ path('app_booking_create_step_3_refresh') }}"
|
||||
hx-trigger="change from:input[type='radio']"
|
||||
hx-target="#form-payment"
|
||||
hx-select="#form-payment"
|
||||
hx-swap="outerHTML">
|
||||
{{ form_row(form.paymentMethod) }}
|
||||
|
||||
{# Conditionally rendered bank account section #}
|
||||
{% if form.bankAccount is defined %}
|
||||
...
|
||||
{% endif %}
|
||||
</div>
|
||||
```
|
||||
|
||||
**Why this pattern:**
|
||||
- Wrapper div captures change events from child radio buttons
|
||||
- Entire section (payment method + bank account fields) gets replaced
|
||||
- `hx-select` extracts only `#form-payment` from full page response
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Currently, the booking creation flow ends at Step 2 (participant details). We need to add Step 3 to collect payment method information before final submission. Users can choose between:
|
||||
1. **Direct Transfer (Überweisung)** - Requires bank account details
|
||||
2. **Direct Debit (Lastschrift)** - Requires bank account details + SEPA mandate
|
||||
|
||||
---
|
||||
|
||||
## Requirements Analysis
|
||||
|
||||
### Payment Methods
|
||||
|
||||
#### 1. Direct Transfer (Überweisung)
|
||||
- User will transfer money manually
|
||||
- Required fields:
|
||||
- Payment method selection (radio/select)
|
||||
- No bank details needed (invoice will show our account for transfer)
|
||||
|
||||
#### 2. Direct Debit (Lastschrift)
|
||||
- Automatic withdrawal from user's account
|
||||
- Required fields:
|
||||
- IBAN (validated format)
|
||||
- Account holder name (text)
|
||||
- SEPA mandate checkbox (required agreement)
|
||||
- Optional fields:
|
||||
- Bank name (text)
|
||||
|
||||
### Validation Requirements
|
||||
|
||||
**IBAN Validation:**
|
||||
- Format: Country code (2 letters) + Check digits (2 digits) + BBAN (up to 30 alphanumeric)
|
||||
- German IBAN: DE + 2 digits + 18 digits (total 22 characters)
|
||||
- International: Support common EU countries
|
||||
- Checksum validation (mod 97 algorithm) via Symfony's `@Assert\Iban`
|
||||
- Display formatted with spaces (DE12 3456 7890 1234 5678 90)
|
||||
|
||||
**Account Holder Name:**
|
||||
- Must match participant/applicant name or be explicitly confirmed
|
||||
- Min length: 2 characters
|
||||
- Max length: 70 characters (SEPA standard)
|
||||
- Allowed: Letters, spaces, hyphens, apostrophes
|
||||
|
||||
**Bank Name:**
|
||||
- Optional but recommended
|
||||
- Max length: 70 characters
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
Step 1 (Room Selection)
|
||||
↓
|
||||
Step 2 (Participant Details)
|
||||
↓
|
||||
Step 3 (Payment Method) ← NEW
|
||||
↓
|
||||
Final Submission to API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Create Bank Account DTO
|
||||
|
||||
#### Task 1.1: Create BankAccountDto
|
||||
**Status:** ✅ Simplified - BIC omitted
|
||||
**Files:**
|
||||
- `src/Form/Model/BankAccountDto.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create DTO class with properties:
|
||||
- `?string $iban` - IBAN with spaces stripped for storage
|
||||
- `?string $accountHolder` - Account holder name
|
||||
- `?string $bankName` - Name of the bank (optional)
|
||||
- `bool $sepaMandateAccepted = false` - SEPA mandate checkbox
|
||||
- [ ] Add Symfony validation constraints:
|
||||
- `@Assert\Iban()` for IBAN
|
||||
- `@Assert\NotBlank()` for IBAN and account holder
|
||||
- `@Assert\Length(min: 2, max: 70)` for account holder
|
||||
- `@Assert\Length(max: 70)` for bank name
|
||||
- `@Assert\IsTrue()` for SEPA mandate
|
||||
- [ ] Add methods:
|
||||
- `getFormattedIban()` - Returns IBAN with spaces for display
|
||||
- `getIbanWithoutSpaces()` - Returns IBAN without spaces for storage/API
|
||||
|
||||
**Notes:**
|
||||
- Public properties (no constructor promotion needed for DTOs)
|
||||
- BIC omitted (not required for German IBANs since 2016)
|
||||
- Follow project DTO patterns
|
||||
|
||||
---
|
||||
|
||||
#### Task 1.2: Create PaymentDto
|
||||
**Files:**
|
||||
- `src/Form/Model/PaymentDto.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create DTO class with properties:
|
||||
- `?string $paymentMethod` - 'transfer' or 'debit'
|
||||
- `?BankAccountDto $bankAccount` - Bank account details (nullable)
|
||||
- [ ] Add validation:
|
||||
- `@Assert\Choice(choices: ['transfer', 'debit'])`
|
||||
- `@Assert\Valid()` for bankAccount when debit selected
|
||||
- [ ] Add method `requiresBankAccount(): bool` - returns true if debit
|
||||
- [ ] Add getter/setter methods
|
||||
|
||||
**Notes:**
|
||||
- Bank account only required for direct debit
|
||||
- Validation must be conditional based on payment method
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Extend BookingCreateDto
|
||||
|
||||
#### Task 2.1: Add Payment Property to BookingCreateDto
|
||||
**Files:**
|
||||
- `src/Form/Model/BookingCreateDto.php`
|
||||
|
||||
**Actions:**
|
||||
- [ ] Add property: `public PaymentDto $payment`
|
||||
- [ ] Initialize in constructor: `$this->payment = new PaymentDto()`
|
||||
- [ ] Ensure serialization works for session storage
|
||||
|
||||
**Notes:**
|
||||
- Payment data stored in session alongside participant data
|
||||
- Must survive page navigation
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Create Step 3 Form Types
|
||||
|
||||
#### Task 3.1: Create BankAccountType
|
||||
**Status:** ✅ Simplified - BIC omitted
|
||||
**Files:**
|
||||
- `src/Form/BankAccountType.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create form type for `BankAccountDto`
|
||||
- [ ] Add fields:
|
||||
- `iban` - TextType with formatting help text (monospace font)
|
||||
- `accountHolder` - TextType (required)
|
||||
- `bankName` - TextType (optional)
|
||||
- `sepaMandateAccepted` - CheckboxType with rich label (SEPA text)
|
||||
- [ ] Add data transformer for IBAN:
|
||||
- Strip spaces from IBAN on submit
|
||||
- Format IBAN with spaces for display
|
||||
- [ ] Add help text with IBAN format example: "DE12 3456 7890 1234 5678 90"
|
||||
- [ ] Style SEPA mandate as prominent checkbox with simple legal text
|
||||
|
||||
**Notes:**
|
||||
- Use monospace font for IBAN field
|
||||
- Add inline validation feedback
|
||||
- SEPA text: Simple generic authorization text (no Creditor ID needed)
|
||||
|
||||
---
|
||||
|
||||
#### Task 3.2: Create PaymentType
|
||||
**Files:**
|
||||
- `src/Form/PaymentType.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create form type for `PaymentDto`
|
||||
- [ ] Add field: `paymentMethod` - ChoiceType (expanded radios)
|
||||
- Choice 1: 'transfer' → "Überweisung"
|
||||
- Choice 2: 'debit' → "Lastschrift"
|
||||
- [ ] Add field: `bankAccount` - BankAccountType (conditional)
|
||||
- [ ] Add form events to show/hide bank account fields:
|
||||
- `PRE_SET_DATA` - Add bank account if debit selected
|
||||
- `PRE_SUBMIT` - Add bank account if debit submitted
|
||||
- [ ] Add JavaScript/HTMX to toggle bank account section visibility
|
||||
|
||||
**Notes:**
|
||||
- Bank account section hidden when transfer selected
|
||||
- Use Stimulus controller for toggle behavior
|
||||
- Smooth transition when switching payment methods
|
||||
|
||||
---
|
||||
|
||||
#### Task 3.3: Create BookingCreateStep3Type
|
||||
**Files:**
|
||||
- `src/Form/BookingCreateStep3Type.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create form type for `BookingCreateDto` (uses payment property)
|
||||
- [ ] Add field: `payment` - PaymentType
|
||||
- [ ] Set data_class to `BookingCreateDto::class`
|
||||
- [ ] Add validation groups: `['payment']`
|
||||
|
||||
**Notes:**
|
||||
- Follows same pattern as Step1Type and Step2Type
|
||||
- Validates only payment-related fields
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Create Step 3 Controller
|
||||
|
||||
#### Task 4.1: Create CreateStep3Controller
|
||||
**Files:**
|
||||
- `src/Controller/Booking/CreateStep3Controller.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create controller with two actions:
|
||||
- `step3()` - Display form (GET)
|
||||
- `processStep3()` - Process form (POST)
|
||||
- [ ] Load `BookingCreateDto` from session
|
||||
- [ ] Create form with `BookingCreateStep3Type`
|
||||
- [ ] On valid submission:
|
||||
- Update session with payment data
|
||||
- Redirect to confirmation/summary page (or direct to API submission)
|
||||
- [ ] Add "Back to Step 2" button
|
||||
- [ ] Add route: `/booking/create/step-3`
|
||||
- [ ] Add access control: `@IsGranted('IS_AUTHENTICATED_ANONYMOUSLY')`
|
||||
|
||||
**Notes:**
|
||||
- Session key: same as Step 1/2 (`booking_create_dto`)
|
||||
- Validate that Steps 1 & 2 are complete before showing Step 3
|
||||
- Clear session on final submission
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Create Step 3 Template
|
||||
|
||||
#### Task 5.1: Create step_3.html.twig
|
||||
**Files:**
|
||||
- `templates/booking/create_step_3.html.twig` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create template extending base layout
|
||||
- [ ] Add progress indicator (Step 1 → Step 2 → **Step 3** → Confirmation)
|
||||
- [ ] Add payment method selection with clear labels
|
||||
- [ ] Add conditional bank account section:
|
||||
- Hidden by default
|
||||
- Shows when direct debit selected
|
||||
- Smooth CSS transition
|
||||
- [ ] Add form with:
|
||||
- Payment method radios (large, clear)
|
||||
- Bank account fields (conditional)
|
||||
- SEPA mandate checkbox with legal text
|
||||
- "Back" and "Continue" buttons
|
||||
- [ ] Add Stimulus controller for payment method toggle
|
||||
- [ ] Add inline validation feedback
|
||||
- [ ] Add help text for IBAN format examples
|
||||
|
||||
**Notes:**
|
||||
- Clear visual hierarchy: payment method choice prominent
|
||||
- Bank account section visually grouped
|
||||
- SEPA text: "Ich ermächtige [Company] Zahlungen von meinem Konto mittels Lastschrift einzuziehen..."
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Update Navigation Flow
|
||||
|
||||
#### Task 6.1: Update CreateStep2Controller
|
||||
**Files:**
|
||||
- `src/Controller/Booking/CreateStep2Controller.php`
|
||||
|
||||
**Actions:**
|
||||
- [ ] Change successful submission redirect:
|
||||
- FROM: Confirmation page
|
||||
- TO: `app_booking_create_step_3`
|
||||
- [ ] Keep session data intact (don't clear)
|
||||
|
||||
**Notes:**
|
||||
- Simple redirect change
|
||||
- Session persistence already in place
|
||||
|
||||
---
|
||||
|
||||
#### Task 6.2: Add Navigation Helpers
|
||||
**Files:**
|
||||
- `src/Service/BookingSessionService.php` (new, optional)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create service to manage booking session state (optional)
|
||||
- [ ] Add methods:
|
||||
- `getBookingDto(): ?BookingCreateDto`
|
||||
- `saveBookingDto(BookingCreateDto $dto): void`
|
||||
- `clearBookingSession(): void`
|
||||
- `validateStepAccess(int $step): bool` - Check prerequisite steps
|
||||
- [ ] Inject into controllers
|
||||
|
||||
**Notes:**
|
||||
- Optional improvement for cleaner controller code
|
||||
- Can be deferred if time-constrained
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: Add Validation & Error Handling
|
||||
|
||||
**Status:** ✅ Simplified - Using Symfony's built-in validators only
|
||||
|
||||
**Notes:**
|
||||
- Symfony's `@Assert\Iban` is sufficient for IBAN validation
|
||||
- No custom validators needed
|
||||
- German error messages configured in translation files
|
||||
|
||||
---
|
||||
|
||||
### Phase 8: Add SEPA Mandate Text Management
|
||||
|
||||
#### Task 8.1: Create SEPA Mandate Template
|
||||
**Files:**
|
||||
- `templates/booking/_sepa_mandate_text.html.twig` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create reusable template partial with SEPA mandate text
|
||||
- [ ] Include variables:
|
||||
- Company name
|
||||
- Creditor ID (SEPA Gläubiger-ID)
|
||||
- Mandate reference (generated per booking)
|
||||
- [ ] Format as legal text with proper line breaks
|
||||
- [ ] Add checkbox label referencing this text
|
||||
|
||||
**Notes:**
|
||||
- Text must be legally compliant
|
||||
- Consult legal team for exact wording
|
||||
- Consider making mandate reference visible to user
|
||||
|
||||
---
|
||||
|
||||
### Phase 9: Frontend Enhancements
|
||||
|
||||
#### Task 9.1: Create Payment Toggle Stimulus Controller
|
||||
**Files:**
|
||||
- `assets/controllers/payment_toggle_controller.js` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create Stimulus controller to toggle bank account visibility
|
||||
- [ ] Listen to payment method radio change
|
||||
- [ ] Show/hide bank account section with smooth transition
|
||||
- [ ] Clear bank account fields when switching to transfer
|
||||
- [ ] Add/remove required attributes dynamically
|
||||
|
||||
**Notes:**
|
||||
- Use CSS transitions for smooth UX
|
||||
- Ensure accessibility (aria attributes)
|
||||
- Works without JavaScript (progressive enhancement)
|
||||
|
||||
---
|
||||
|
||||
#### Task 9.2: Add IBAN Formatting Helper
|
||||
**Files:**
|
||||
- `assets/controllers/iban_formatter_controller.js` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create Stimulus controller for IBAN input
|
||||
- [ ] Format IBAN with spaces as user types: `DE12 3456 7890 1234 5678 90`
|
||||
- [ ] Strip spaces on submit
|
||||
- [ ] Show character count/validation status
|
||||
- [ ] Use monospace font for input
|
||||
|
||||
**Notes:**
|
||||
- Real-time formatting improves UX
|
||||
- Visual feedback for correct format
|
||||
- Consider using library: `iban-formatter`
|
||||
|
||||
---
|
||||
|
||||
### Phase 10: Testing
|
||||
|
||||
#### Task 10.1: Create Unit Tests
|
||||
**Files:**
|
||||
- `tests/Form/Model/BankAccountDtoTest.php` (new)
|
||||
- `tests/Form/Model/PaymentDtoTest.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Test IBAN validation (valid/invalid formats)
|
||||
- [ ] Test conditional validation (bank account required for debit)
|
||||
- [ ] Test SEPA mandate validation
|
||||
- [ ] Test account holder name validation
|
||||
|
||||
**Notes:**
|
||||
- Cover edge cases: empty, malformed, international IBANs
|
||||
- Test both valid and invalid inputs
|
||||
|
||||
---
|
||||
|
||||
#### Task 10.2: Create Integration Tests
|
||||
**Files:**
|
||||
- `tests/Controller/Booking/CreateStep3ControllerTest.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Test form display
|
||||
- [ ] Test transfer selection (no bank account)
|
||||
- [ ] Test debit selection (requires bank account)
|
||||
- [ ] Test validation errors
|
||||
- [ ] Test session persistence
|
||||
- [ ] Test navigation (back to Step 2)
|
||||
|
||||
**Notes:**
|
||||
- Use test client to simulate user interaction
|
||||
- Verify session state after each action
|
||||
|
||||
---
|
||||
|
||||
### Phase 11: Update API Submission
|
||||
|
||||
#### Task 11.1: Update BookingDataProcessor
|
||||
**Status:** ✅ Simplified - BIC omitted from API payload
|
||||
**Files:**
|
||||
- `src/BusProNet/DataProcessor/BookingDataProcessor.php`
|
||||
|
||||
**Actions:**
|
||||
- [ ] Update `createBookingRequestPayload()` method (when implemented in Phase 5)
|
||||
- [ ] Add payment data to API payload:
|
||||
- Payment method (`zahlart`)
|
||||
- Bank account details (if debit):
|
||||
- IBAN (`iban`)
|
||||
- Account holder (`kontoinhaber`)
|
||||
- Bank name (`bankname`) - optional
|
||||
- [ ] Map payment method to API codes:
|
||||
- 'transfer' → 'UE' (or appropriate API code)
|
||||
- 'debit' → 'LS' (or appropriate API code)
|
||||
|
||||
**Notes:**
|
||||
- BIC omitted (not required for SEPA since 2016)
|
||||
- Check BPN API documentation for exact field names
|
||||
- SEPA mandate reference may need to be generated
|
||||
|
||||
---
|
||||
|
||||
#### Task 11.2: Store SEPA Mandate Reference
|
||||
**Files:**
|
||||
- `src/Service/SepaMandateService.php` (new)
|
||||
|
||||
**Actions:**
|
||||
- [ ] Create service to generate unique SEPA mandate references
|
||||
- [ ] Format: `[PREFIX]-[BOOKING_ID]-[TIMESTAMP]`
|
||||
- [ ] Store reference in booking data
|
||||
- [ ] Make available for PDF invoice generation
|
||||
|
||||
**Notes:**
|
||||
- Mandate reference must be unique and traceable
|
||||
- Consider using UUID or sequential ID
|
||||
|
||||
---
|
||||
|
||||
## Database Considerations
|
||||
|
||||
### SEPA Mandate Storage
|
||||
|
||||
**Option 1: Store in Booking API Data**
|
||||
- Mandate reference sent to BPN API
|
||||
- Stored in BPN system
|
||||
- Retrieved with booking data
|
||||
|
||||
**Option 2: Local Database Table**
|
||||
- Create `sepa_mandates` table
|
||||
- Store: mandate_id, booking_id, iban, date_signed, status
|
||||
- Allows local tracking and reporting
|
||||
|
||||
**Recommendation:** Start with Option 1 (API storage), add Option 2 if needed for compliance/reporting
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **HTTPS Required:** Bank details must be transmitted over HTTPS only
|
||||
2. **Session Security:** Ensure session data encrypted
|
||||
3. **Input Sanitization:** Strip/validate all bank account inputs
|
||||
4. **CSRF Protection:** Ensure form has CSRF token
|
||||
5. **Rate Limiting:** Prevent brute force on payment form
|
||||
6. **Audit Trail:** Log payment method changes
|
||||
|
||||
---
|
||||
|
||||
## UX Considerations
|
||||
|
||||
1. **Clear Labels:** "Überweisung" and "Lastschrift" with explanations
|
||||
2. **Visual Feedback:** Show selected payment method prominently
|
||||
3. **Inline Validation:** Real-time IBAN format checking
|
||||
4. **Error Messages:** Clear, actionable German error messages
|
||||
5. **Help Text:** Examples of valid IBAN formats
|
||||
6. **Progress Indicator:** Show user is on Step 3 of 4
|
||||
7. **Mobile Friendly:** Large touch targets for payment method selection
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Validation Errors
|
||||
- Show inline next to field
|
||||
- Highlight invalid fields in red
|
||||
- Provide clear guidance on how to fix
|
||||
|
||||
### Session Errors
|
||||
- If session expired, redirect to Step 1 with message
|
||||
- Preserve as much data as possible
|
||||
|
||||
### API Errors
|
||||
- If payment method not accepted by API, show clear error
|
||||
- Suggest alternative payment method
|
||||
|
||||
---
|
||||
|
||||
## Localization
|
||||
|
||||
All text in German:
|
||||
- **Überweisung:** Direct transfer
|
||||
- **Lastschrift:** Direct debit
|
||||
- **IBAN:** Internationale Bankkontonummer
|
||||
- **BIC:** Bank Identifier Code
|
||||
- **Kontoinhaber:** Account holder
|
||||
- **SEPA-Mandat:** SEPA mandate
|
||||
- **Bankname:** Bank name
|
||||
|
||||
Error messages in German with clear instructions.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
### PHP Libraries
|
||||
- `symfony/validator` (already installed)
|
||||
- `symfony/form` (already installed)
|
||||
- Consider: `iban-validation/iban` for enhanced IBAN validation (optional)
|
||||
|
||||
### JavaScript Libraries
|
||||
- Stimulus (already installed)
|
||||
- Consider: Custom IBAN formatter or library
|
||||
|
||||
### No new major dependencies required
|
||||
|
||||
---
|
||||
|
||||
## Migration Path
|
||||
|
||||
1. Implement in order: Phases 1 → 2 → 3 → 4 → 5 → 6
|
||||
2. Test each phase before moving to next
|
||||
3. Phases 7-11 can be done in parallel after Phase 6
|
||||
4. Deploy behind feature flag initially (optional)
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **SEPA Creditor ID:** What is the company's SEPA Gläubiger-ID?
|
||||
2. **Payment Method Codes:** What are the exact BPN API codes for transfer/debit?
|
||||
3. **Mandate Reference Format:** Any specific format requirements?
|
||||
4. **Invoice Generation:** Does invoice need to show SEPA mandate reference?
|
||||
5. **Default Payment Method:** Should we pre-select one method?
|
||||
6. **International IBANs:** Support only German IBANs or EU-wide?
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] User can select payment method
|
||||
- [ ] Bank account fields shown only for direct debit
|
||||
- [ ] IBAN validated correctly
|
||||
- [ ] SEPA mandate text displayed and accepted
|
||||
- [ ] Payment data stored in session
|
||||
- [ ] Payment data submitted to API
|
||||
- [ ] Session cleared after successful booking
|
||||
- [ ] All validation works (unit + integration tests)
|
||||
- [ ] Mobile-friendly UI
|
||||
- [ ] Accessible (keyboard navigation, screen readers)
|
||||
|
||||
---
|
||||
|
||||
## Timeline Estimate
|
||||
|
||||
- **Phase 1-2:** DTOs & Validation - 2 hours
|
||||
- **Phase 3:** Form Types - 2 hours
|
||||
- **Phase 4:** Controller - 1 hour
|
||||
- **Phase 5:** Template - 2 hours
|
||||
- **Phase 6:** Navigation - 0.5 hour
|
||||
- **Phase 7:** Custom Validators - 1 hour (optional)
|
||||
- **Phase 8:** SEPA Text - 0.5 hour
|
||||
- **Phase 9:** Frontend - 2 hours
|
||||
- **Phase 10:** Testing - 2 hours
|
||||
- **Phase 11:** API Integration - 1 hour
|
||||
|
||||
**Total:** ~14 hours (can be reduced if some phases skipped/simplified)
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- SEPA Direct Debit Scheme: https://www.europeanpaymentscouncil.eu/
|
||||
- IBAN Validation: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
||||
- Symfony Form Events: https://symfony.com/doc/current/form/events.html
|
||||
- Symfony Validation: https://symfony.com/doc/current/validation.html
|
||||
|
||||
---
|
||||
|
||||
**End of Document**
|
||||
Reference in New Issue
Block a user