Skip to content

Latest commit

 

History

History
68 lines (49 loc) · 2.75 KB

File metadata and controls

68 lines (49 loc) · 2.75 KB

Contributing

Setup

mvn -B verify   # compiles both modules and runs the tests

The build multi-targets a paylink-core module and a paylink-spring-boot-starter module, both on Java 17.

The one thing to get right: signature parity

The SDK's core job is reproducing a byte-exact, order-sensitive HMAC that the server rebuilds independently. The server signs base64(hmac_sha256(implode('', Arr::except($request->validated(), [...])))), and Laravel's validated() returns fields in rules() order, skipping absent ones.

So the order of fields in FieldOrders must match the order of keys in the endpoint's FormRequest rules(), minus token/signature (and payment_mode, added with FieldSpec.unsigned(...)).

When you touch an endpoint:

  1. Open the matching FormRequest under app/Http/Requests/Application/ExternalPaymentIntegration/ in the PayLink repo and read rules() top to bottom.
  2. Mirror that order in the EndpointSpec. SignedBody.build signs the coerced values in that order — the compiler cannot verify ordering for you.
  3. Add a golden vector.

Golden vectors

paylink-core/src/test/resources/golden-signatures.json is shared with the JS, Python, and .NET SDKs and generated by PHP using the same primitive the server uses. GoldenSignatureTest asserts both that Signature.build(values) produces expected and that SignedBody.build derives the same signature from structured input.

Adding a field to an endpoint without adding a golden case leaves that field's position untested.

Webhooks sign by opt-out — the mirror-image trap

Requests sign by opt-in: FieldOrders lists exactly what gets signed. Webhooks are the opposite. PaymentIntegrationWebhookJob copies the whole payload and unset()s a fixed exclusion list before hashing, so a field is signed if added before that unset() and unsigned if added after (as auth_code is). The OPTIONAL_SIGNED list in DefaultWebhooks must mirror that decision.

Dependency injection

The SDK is DI-first: PaylinkClient is an interface you depend on and inject. The paylink-spring-boot-starter provides the container wiring. Prefer constructor injection; no static singletons or new'd dependencies inside methods.

Style

  • Javadoc (/** ... */) above members only. No comments inside method bodies.
  • Format numbers with locale-independent primitives (Long.toString, BigDecimal.toPlainString); a machine's locale must never shift a signature.

Releasing

  1. Bump the <version> in the parent POM and modules, and Version.VALUE in paylink-core/.../internal/Version.java (the User-Agent would otherwise report a stale version). Keep them in sync.
  2. Update CHANGELOG.md.
  3. Tag v<version> and publish to Maven Central.