Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .api-sync/known-divergences.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"$schema": "Recorded, reasoned, owned divergences that state reconciliation must not silently paper over. Two kinds: `enumValues` is a spec enum member whose value does not match any SDK case value even though the SDK models that enum (usually because an SDK case value has a typo/format bug) -- the fix is to CORRECT the existing case, not to add a near-duplicate new one, so scripts/api-sync.php treats a listed (enum, specValue) pair as satisfied rather than pending drift. `fields` covers two situations, both keyed by {schema, field}: (a) a spec property backed by a constrained enum but modeled as a plain untyped field in the SDK, so there is no case-completeness gap to begin with; (b) a genuine type-representation mismatch between the spec's declared type and the SDK's declared PHP property type on an already-modeled field, recorded here rather than silently accepted or blindly auto-fixed, since correcting it is a deliberate code change a human must make. Distinct from .api-sync/unmodeled.json, which is for properties absent from the SDK outright.",
"enumValues": [
{
"enum": "BankAccountType",
"specValue": "saving",
"sdkCase": "SAVINGS",
"sdkValue": "savings",
"reason": "LIVE DEFECT (not fixed in this PR): the API's wire value is 'saving' (singular); this SDK's case value is 'savings' (plural), so a PHP backed enum currently serializes the wrong string and a real 'saving' account_type response would fail to parse via BankAccountType::from(). To be fixed in its own deliberate PR that corrects the case value directly instead of adding a second near-duplicate case.",
"owner": "[email protected]"
},
{
"enum": "BankAccountType",
"specValue": null,
"sdkCase": "TED",
"sdkValue": "ted",
"reason": "SDK-only case with no counterpart in the spec's account_type enum (which only has checking/saving). Pre-existing, unrelated to account_type modeling; not touched here.",
"owner": "[email protected]"
},
{
"enum": "EstimatedAnnualRevenue",
"specValue": "250000000_plus",
"sdkCase": "RANGE_2500000000_PLUS",
"sdkValue": "2500000000_plus",
"reason": "The API's top revenue bucket is '250000000_plus' (250 million); this SDK's case value has an extra digit, '2500000000_plus' (2.5 billion), so a real API response for the top bucket would fail to parse via EstimatedAnnualRevenue::from(). Same shape of bug as BankAccountType.saving/savings -- to be fixed in its own deliberate PR that corrects the case value, not by adding a second near-duplicate case.",
"owner": "[email protected]"
}
],
"fields": [
{
"schema": "CustomerOut",
"field": "kyc_status",
"reason": "Spec constrains kyc_status to an 8-value enum (verifying/approved/rejected/deprecated/pending_review/awaiting_contract/compliance_request/approved_rfi). This SDK models it as a plain `string` on BaseCustomer, not a backed enum, so every spec value already parses without error -- there is no case-completeness gap in code today. Recorded for visibility only.",
"owner": "[email protected]"
},
{
"schema": "QuoteOut",
"field": "expires_at",
"reason": "TYPE MISMATCH, pre-existing and identical between the last two spec snapshots (not new drift): spec declares expires_at as `number|null` (a float), CreateQuoteResponse declares `public int $expiresAt` with no cast in fromArray. In practice this is a Unix timestamp (spec example: 1712958191, always a whole number), so json_decode already hands back a PHP int and the mismatch has not surfaced -- but a genuinely fractional or null value from the API would throw a TypeError under strict_types. Not fixed here: changing the property type is a deliberate decision, not a mechanical field add.",
"owner": "[email protected]"
},
{
"schema": "CreatePayinQuoteOut",
"field": "expires_at",
"reason": "Same pre-existing mismatch as QuoteOut.expires_at, on the payin-quote equivalent (CreatePayinQuoteResponse). Here the field IS cast, `expiresAt: (int) $data['expires_at']`, so a fractional value would silently truncate rather than throw -- a real risk in disguise if the API ever returns one.",
"owner": "[email protected]"
},
{
"schema": "PayinOut",
"field": "billing_fee_amount",
"reason": "LIVE DEFECT, VERIFIED BY RUNNING CODE (not fixed in this PR): spec declares billing_fee_amount as `number|null` (a fee in cents), Payin declares `public ?string $billingFeeAmount` with no cast in fromArray. Calling Payin::fromArray() with any present, non-null billing_fee_amount throws `TypeError: BlindPay\\SDK\\Resources\\Payins\\Payin::__construct(): Argument #35 ($billingFeeAmount) must be of type ?string, int given` -- reproduced directly, not theoretical. Every payin response where this fee is populated (end of month) fails to parse. Not fixed here: choosing the correct type (float? string with an explicit cast?) is a deliberate decision, pre-existing and identical between the last two spec snapshots (not new drift).",
"owner": "[email protected]"
},
{
"schema": "PaginationMetadata",
"field": "next_page",
"reason": "LIVE DEFECT, VERIFIED BY RUNNING CODE (not fixed in this PR): spec declares next_page as a nullable STRING cursor (`type: [\"string\",\"null\"]`, example `\"pi_123\"`, the ID of the first item in the next page), but PaginationMetadata declares `public int $nextPage` with no cast. Calling PaginationMetadata::fromArray() with any present, non-null next_page throws `TypeError: BlindPay\\SDK\\Types\\PaginationMetadata::__construct(): Argument #2 ($nextPage) must be of type int, string given` -- reproduced directly, not theoretical. PaginationMetadata backs the customers/payouts/payins/transfers list responses, so ANY paginated list response that actually has a next page fails to parse through this SDK. Not fixed here: the property type needs to become `?string`, a deliberate code change with its own PR.",
"owner": "[email protected]"
},
{
"schema": "PaginationMetadata",
"field": "prev_page",
"reason": "Same live defect as PaginationMetadata.next_page, on the previous-page cursor (`public int $prevPage`, spec declares a nullable string). Not independently re-verified by execution but identical shape and identical code path.",
"owner": "[email protected]"
}
]
}
Loading
Loading