Commit Graph
6 Commits
Author SHA1 Message Date
libretech f599b2c84b feat(logging): describe an error by its name, message and status code
describeError keeps an error's name, the first line of its message, cut
to 200 characters, and its HTTP status code. An error of the PayPal SDK
or of axios carries its request, whose Authorization header holds the
PayPal client credentials or an access token, so the error object itself
must not reach the log. The SDK's response validation error quotes the
response values on the lines after the first.

Refs libretech/mp#71
2026-10-09 01:12:20 +02:00
libretech 4d77998b51 feat(checkout): verify the approved PayPal order and its capture against the order
verifyApprovedOrder accepts PayPal's order only if it is APPROVED, its
first purchase unit's custom_id is the order's uuid, and its amount is the
order total in EUR. verifyCompletedCapture accepts the order after the
capture only if it and its first capture are COMPLETED for the order
total in EUR, and returns the capture id. Both read PayPal's Orders v2
JSON in snake_case, as PayPalApi parses the response body.

Money is compared in integer cents: PayPal's "12.34" is parsed strictly,
the order total is Math.round(total * 100). For every total up to
3000.00 the amount the checkout sends (total.toFixed(2)) parses to the
cents the capture compares.

A rejection reason repeats only PayPal's statuses, currency codes and
amounts, never the payer's or the shipping data of the response.

Refs libretech/mp#71
2026-10-09 01:12:20 +02:00
libretech 88f4ee75d7 feat(order): lock an order for the customer once its payment is authorised
Refs libretech/mp#71
2026-10-08 22:54:59 +02:00
libretech b9670b7229 feat(order): parse the customer's order update against the checkout fields
parseCustomerUpdate accepts { data } with a subset of the seven fields the
shop's checkout sends: email, acceptedTermsAndConditionsAt, invoiceAddress,
deliveryAddress, invoiceAddressStructured, deliveryAddressStructured and
delivery. Any other key, at the top level, in data or in a structured
address, rejects the whole body, and the errors name the fields without
repeating their values.

The terms are recorded at the server's time whenever the client sends a
true value: the invoice is dated by it.

Refs libretech/mp#71
2026-10-08 22:54:59 +02:00
libretech 9b0c6af8b7 Test the checkout code; publish images only for releases (#5)
build / test (push) Successful in 7s
build / build (push) Skipped
- src/checkout/ (PayPal order request, pricing, VAT) and src/logging/format.ts
  hold the checkout's pure code, moved out of PayPalApi, the product service
  and config without a behaviour change.
- npm test runs node:test on the TypeScript sources (type stripping), with no
  dependencies: 21 tests, 13 pin today's output, 8 describe the correct
  behaviour for the known defects of libretech/mp#67
  and are marked todo.
- CI: the tests run in the image's base (node:22.14.0-alpine) for every pull
  request, push to main and release tag. The image is built for pull requests
  and built and published only for v* tags; main no longer publishes :main
  and :sha-* images.

Refs libretech/mp#71

Co-authored-by: Michael Czechowski <mail@librete.ch>
2026-10-08 22:36:47 +02:00
Michael Czechowski 32a296baf2 feat: extract cms from mp/cms — initial libreshop/cms
Build and publish / build (push) Failing after 17s
Source moved verbatim from mp/cms/ on 2026-04-29; mp was the first
concrete adapter consuming the libreshop toolkit. Builds and publishes
git.librete.ch/libreshop/cms on every main / v* push via the standard
.gitea/workflows/build.yml shared across libreshop components.
2026-04-29 17:48:30 +02:00