Skip to content

feat: add payment reconciliation cron job#2

Open
feelautom wants to merge 1 commit intowearestancer:mainfrom
feelautom:feat/reconciliation-cron
Open

feat: add payment reconciliation cron job#2
feelautom wants to merge 1 commit intowearestancer:mainfrom
feelautom:feat/reconciliation-cron

Conversation

@feelautom
Copy link

Problem

Stancer does not support webhooks. When a customer completes payment on the Stancer hosted page, the validation.php controller handles the return and creates the PrestaShop order. However, if the redirect never happens (browser closed, network error, session expired), the local payment stays pending indefinitely — no order is ever created, even though the payment was captured by Stancer.

This affects all shops running in redirect mode and results in captured payments with no corresponding order on the merchant side.

Solution

Add a reconciliation cron job that polls the Stancer API every 15 minutes for pending payments older than 15 minutes and creates the PrestaShop order when the payment has been captured.

Implementation

New file: controllers/front/cron.php

Class StancerCronModuleFrontController accessible at:

https://example.com/module/stancer/cron?token=STANCER_CRON_TOKEN

Recommended crontab:

*/15 * * * * curl -s "https://example.com/module/stancer/cron?token=TOKEN" > /dev/null

Logic:

  • Queries ps_stancer_payment for rows with status = pending, no id_order, and date_add older than 15 minutes (threshold allows in-progress redirect flows to complete first)
  • For each payment, fetches the real status from the Stancer API
  • to_capture or captured → calls module->validateOrder() to create the PS order, saves id_order on the local record, logs success
  • refused, failed, canceled, expired → logs a warning, marks the payment as processed (no order will be created)
  • capture, capture_sent and other intermediate statuses → skipped until next run (these do not reliably map to a valid PS order state via getOrderState())
  • Returns a JSON response: { processed, reconciled, skipped, errors }
  • Endpoint secured with hash_equals() token comparison

Modified: stancer.php

  • getConfigurationsList(): adds STANCER_CRON_TOKEN (generated once on install via random_bytes(16), stored as a hidden configuration key)
  • $hooks: adds actionCronJob for compatibility with the PrestaShop CronJobs module (ps_cronjobs)
  • hookActionCronJob(): implements the same reconciliation logic for shops using ps_cronjobs as an alternative to a server cron job

Notes

  • The 15-minute threshold prevents interference with in-progress redirect flows
  • StancerApiPayment::getOrderState() is reused as-is — only to_capture and captured are handled as they reliably map to PS_OS_PAYMENT
  • The STANCER_CRON_TOKEN is generated on install and never exposed in the admin UI — merchants retrieve it from the PS configuration table or via a future admin setting
  • All errors are logged to PrestaShop's logger under the StancerCronModuleFrontController object type
  • Status normalization handles both enum returns (PHP 8.1+ SDK) and plain string returns for forward compatibility

Stancer does not support webhooks. If a customer closes the browser or
loses connectivity after payment, the redirect to the validation
controller never fires and the local payment stays "pending" forever —
no PrestaShop order is created even though the payment was captured.

This commit adds a reconciliation mechanism with two entry points:

controllers/front/cron.php — StancerCronModuleFrontController
  Accessible at /module/stancer/cron?token=STANCER_CRON_TOKEN.
  Queries ps_stancer_payment for rows with status="pending", no
  associated order, and date_add older than 15 minutes. For each:
  - Fetches the real payment status from the Stancer API.
  - If to_capture or captured: calls module->validateOrder() to create
    the PrestaShop order and saves the new id_order on the local record.
    (CAPTURE and CAPTURE_SENT are intermediate states — skipped until
    the status progresses to a value that maps to PS_OS_PAYMENT in
    StancerApiPayment::getOrderState().)
  - If refused / failed / canceled / expired: logs a PS warning. No
    order is created for this payment.
  - Returns a JSON summary { processed, reconciled, skipped, errors }.
  The endpoint is secured with hash_equals() token comparison.

stancer.php
  - Adds STANCER_CRON_TOKEN to getConfigurationsList() (generated once
    on install via random_bytes(), stored as a hidden setting).
  - Adds actionCronJob to the hooks list for compatibility with the
    PrestaShop CronJobs module (ps_cronjobs) as an alternative trigger.
  - Adds hookActionCronJob() implementing the same reconciliation logic
    for shops using ps_cronjobs instead of a server cron job.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant