Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payment request a new way to handle payments #16237

Open
3 of 7 tasks
Prometee opened this issue May 8, 2024 · 0 comments
Open
3 of 7 tasks

Payment request a new way to handle payments #16237

Prometee opened this issue May 8, 2024 · 0 comments
Labels
API APIs related issues and PRs. Feature New feature proposals. RFC Discussions about potential changes or new features.

Comments

@Prometee
Copy link
Contributor

Prometee commented May 8, 2024

State of the art

The current order payment processing is made by Payum: most developers found it hard to get into it. Payum is usually blamed because it is not using already well known concepts and the resulting development of a new Payum gateway can become painful sometime.
However Payum is still the only open source PHP payment library which can bring any payment method to any PHP project.

Sylius needs a new way to handle payments with new ways like :

  • By making API calls which interact with any existing Payum payment gateways
  • By having a way to process a payment asynchronously or synchronously depending on the way the payment method is working.

Feature explanation

The Sylius Core team come up with a new concept called "Payment Request", this new feature will allows easier payment processing integration for the UI and the API.
It will supports Payum on the first version it will be shipped, but the goal is to make it optional at the end.

What about the Payum support ?

Since Payum needs an HTTP request and sometime even a session, it's tricky to fully support all the ways the currently available Payum gateways are doing thing.
For example, it's not possible for the PaymentRequest feature to support Payum gateways which are :

  • Doing weird things like an echo "..."; or making a die; instead of using the RenderTemplate Payum request.
  • Reading vars like $_REQUEST, $_GET or $_POST instead of requesting GetHttpRequest.
  • Making a redirection without using the specific Payum way which is to throw an Exception implementing ReplyInterface.

What changes are expected on the PaymentMethod to avoid using Payum ?

The actual Sylius PaymentMethod is relying on a type and it will be used to know if it's a Payum one or not.
The current concept is to define some tagged services to provide custom symfony/messenger messages for each actions the payment handler will have to supports.

For example, for a specific PaymentMethod we can get each supported actions via those service definitions, then when a PaymentRequest is created the action requested will be dispatched into a custom message and consume asynchronously or synchronously depending on the message configuration.

Examples

Synchronous payment processing

This is a diagram explaining how it would work with a synchronous processing:

%%{
  init: {
    'sequence': {
      'noteAlign': 'left',
      'noteFontFamily': 'Courier New, monospace'
    }
  }
}%%
sequenceDiagram
    title Sylius - Payment Request

    Website->>+Sylius API: POST /shop/payment-request
    Note right of Website: {<br>    "paymentId": 10,<br>    "paymentMethodCode": "offline",<br>    "action": "capture",<br>    "payload": ...<br>}
    Sylius API->>+Payment Method: Dispatch a CAPTURE message
    Payment Method-->>-Sylius API: Response
    Note left of Payment Method: {'responseData': [...]}
    Sylius API->>+Payment Method: Dispatch a STATUS message
    Payment Method-->>-Sylius API: Response
    Sylius API-->>-Website: 
    Note left of Sylius API: {<br>    "hash": '123456789-1234-123-1234',<br>    'state': 'completed',<br>    ...<br>    "responseData": [<br>        'redirectUrlOrSomething': 'https://...'<br>    ]<br>}

Asynchronous processing

%%{
  init: {
    'sequence': {
      'noteAlign': 'left',
      'noteFontFamily': 'Courier New, monospace'
    }
  }
}%%
sequenceDiagram
    title Sylius - Payment Request

    Website->>+Sylius API: POST /shop/payment-request
    Note right of Website: {<br>    "paymentId": 10,<br>    "paymentMethodCode": "offline",<br>    "action": "capture",<br>    "payload": ...<br>}
    Sylius API-->>-Website: 
    Note left of Sylius API: {<br>    "hash": '123456789-1234-123-1234',<br>    'state': 'processing',<br>    ...,<br>    "responseData": []<br>}
    par
        loop Polling until state change
            Website->>+Sylius API: GET /shop/payment-request/{hash}
            Sylius API-->>-Website: 
            Note left of Sylius API: {<br>    "hash": '123456789-1234-123-1234',<br>    'state': 'new',<br>    ...,<br>    "responseData": []<br>}
        end
    and
        Sylius API->>+Payment Method: Dispatch a CAPTURE message
        Payment Method-->>-Sylius API: Response
        Note left of Payment Method: {'responseData': [...]}
        Sylius API->>+Payment Method: Dispatch a STATUS message
        Payment Method-->>-Sylius API: Response
    and
        Website->>+Sylius API: GET /shop/payment-request/{hash}
        Sylius API-->>-Website: 
        Note left of Sylius API: {<br>    "hash": '123456789-1234-123-1234',<br>    'state': 'completed',<br>    ...,<br>    "responseData": [<br>        'redirectUrlOrSomething': 'https://...'<br>    ]<br>}
    end

More ?

In some cases we need to create a PaymentRequest and update some data along the way. This is typically happening when you need to display a form or redirect the customer to a portal.
For example, let's say you are requesting a PaymentRequest for the action="capture" and the payment method will have first to display a form to the customer before actually capturing the payment.
It means that the payment method have to send a PaymentRequest->responseData even if the PaymentRequest->state is not completed.

Here I choose a synchronous example to make it simpler.

%%{
  init: {
    'sequence': {
      'noteAlign': 'left',
      'noteFontFamily': 'Courier New, monospace'
    }
  }
}%%
sequenceDiagram
    title Sylius - Payment Request

    Website->>+Sylius API: POST /shop/payment-request
    Note right of Website: {<br>    "paymentId": 10,<br>    "paymentMethodCode": "offline",<br>    "action": "capture",<br>    "payload": ...<br>}
    Sylius API->>+Payment Method: Dispatch a CAPTURE message
    Payment Method-->>-Sylius API: Response
    Note left of Payment Method: {'responseData': [...]}
    Sylius API->>+Payment Method: Dispatch a STATUS message
    Payment Method-->>-Sylius API: Response
    Sylius API-->>-Website: 
    Note left of Sylius API: {<br>    "hash": '123456789-1234-123-1234',<br>    'state': 'processing',<br>    ...<br>    "responseData": [<br>        'dataRequiredForAForm': [...]<br>    ]<br>}
    Website->>+Sylius API: PUT /shop/payment-request/{hash}
    Note right of Website: {<br>    "payload": {/*data from the submitted form or else*/}<br>}
    Sylius API->>+Payment Method: Dispatch a CAPTURE message
    Payment Method-->>-Sylius API: Response
    Note left of Payment Method: {'responseData': [...]}
    Sylius API->>+Payment Method: Dispatch a STATUS message
    Payment Method-->>-Sylius API: Response
    Sylius API-->>-Website: 
    Note left of Sylius API: {<br>    "hash": '123456789-1234-123-1234',<br>    'state': 'completed',<br>    ...<br>    "responseData":[...]<br>}

Task list

We worked on a first PR : #15502 (closed but another PR with the same content has been merged #15943)
Then a new branch was created after this PR to handle all the future work :
https://github.com/Sylius/Sylius/tree/payment-request

Here is the list of tasks which need to be done to go ahead:

  • [Component] Design new PaymentRequest models.
  • [API] Create new front API endpoints.
  • [Core] Integrate legacy PayPal Payum gateway using PaymentRequest flow (as a POC).
  • [Core] Integrate legacy Stripe Payum gateway using PaymentRequest flow (as a POC).
  • [Admin] Copy Payum GatewayConfig model to Payment component (path to the Payum optional requirement).
  • [API] Refactor duplicate check to a generic service allowing to check if the PaymentReuest should be created or not.
  • [FRONT UI]Integrate PaymentRequest into the UI flow
@GSadee GSadee added Feature New feature proposals. RFC Discussions about potential changes or new features. API APIs related issues and PRs. labels May 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API APIs related issues and PRs. Feature New feature proposals. RFC Discussions about potential changes or new features.
Projects
None yet
Development

No branches or pull requests

2 participants