From 865f8bb5360ca0f140185eae5546ce413ceca6be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 19:20:29 +0000 Subject: [PATCH 1/3] chore: sync repo --- .claude/skills/grid-api/SKILL.md | 379 - .../grid-api/references/account-types.md | 210 - .../skills/grid-api/references/endpoints.md | 187 - .../skills/grid-api/references/workflows.md | 274 - .cursor/commands/transfer-in.md | 0 .devcontainer/Dockerfile | 23 + .devcontainer/devcontainer.json | 20 + .fossa.yml | 6 - .gitattributes | 4 - .github/workflows/ci.yml | 85 + .github/workflows/detect-breaking-changes.yml | 39 + .github/workflows/docs-sync.yml | 165 - .github/workflows/dummy.yml | 26 - .github/workflows/lint.yml | 41 - .github/workflows/openapi-build.yml | 38 - .github/workflows/stainless-action.yml | 59 - .gitignore | 44 +- .markdownlint.json | 9 - .redocly.lint-ignore.yaml | 14 - .redocly.yaml | 31 - .stainless/stainless.yml | 341 - .stainless/workspace.json | 10 - .stats.yml | 4 + CLAUDE.md | 173 - LICENSE | 366 +- Makefile | 33 - README.md | 1363 +- SECURITY.md | 27 + build.gradle.kts | 49 + buildSrc/build.gradle.kts | 12 + buildSrc/src/main/kotlin/grid.java.gradle.kts | 136 + .../src/main/kotlin/grid.kotlin.gradle.kts | 109 + .../src/main/kotlin/grid.publish.gradle.kts | 69 + cli/.gitignore | 9 - cli/README.md | 416 - cli/package-lock.json | 251 - cli/package.json | 31 - cli/src/client.ts | 149 - cli/src/commands/accounts.ts | 242 - cli/src/commands/config.ts | 55 - cli/src/commands/configure.ts | 110 - cli/src/commands/customers.ts | 222 - cli/src/commands/quotes.ts | 198 - cli/src/commands/receiver.ts | 66 - cli/src/commands/sandbox.ts | 102 - cli/src/commands/transactions.ts | 114 - cli/src/commands/transfers.ts | 90 - cli/src/config.ts | 93 - cli/src/index.ts | 94 - cli/src/output.ts | 135 - cli/src/prompt.ts | 35 - cli/src/validation.ts | 94 - cli/tsconfig.json | 19 - gradle.properties | 18 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 251 + gradlew.bat | 94 + grid-kotlin-client-okhttp/build.gradle.kts | 15 + .../api/client/okhttp/GridOkHttpClient.kt | 323 + .../client/okhttp/GridOkHttpClientAsync.kt | 323 + .../grid/api/client/okhttp/OkHttpClient.kt | 268 + grid-kotlin-core/build.gradle.kts | 44 + .../kotlin/com/grid/api/client/GridClient.kt | 141 + .../com/grid/api/client/GridClientAsync.kt | 141 + .../grid/api/client/GridClientAsyncImpl.kt | 254 + .../com/grid/api/client/GridClientImpl.kt | 240 + .../kotlin/com/grid/api/core/AutoPager.kt | 16 + .../com/grid/api/core/AutoPagerAsync.kt | 27 + .../com/grid/api/core/BaseDeserializer.kt | 44 + .../com/grid/api/core/BaseSerializer.kt | 6 + .../main/kotlin/com/grid/api/core/Check.kt | 86 + .../kotlin/com/grid/api/core/ClientOptions.kt | 510 + .../com/grid/api/core/DefaultSleeper.kt | 14 + .../kotlin/com/grid/api/core/ObjectMappers.kt | 178 + .../src/main/kotlin/com/grid/api/core/Page.kt | 33 + .../kotlin/com/grid/api/core/PageAsync.kt | 33 + .../main/kotlin/com/grid/api/core/Params.kt | 16 + .../com/grid/api/core/PhantomReachable.kt | 54 + .../core/PhantomReachableExecutorService.kt | 58 + .../grid/api/core/PhantomReachableSleeper.kt | 21 + .../com/grid/api/core/PrepareRequest.kt | 16 + .../kotlin/com/grid/api/core/Properties.kt | 42 + .../com/grid/api/core/RequestOptions.kt | 45 + .../main/kotlin/com/grid/api/core/Sleeper.kt | 17 + .../main/kotlin/com/grid/api/core/Timeout.kt | 155 + .../main/kotlin/com/grid/api/core/Utils.kt | 102 + .../main/kotlin/com/grid/api/core/Values.kt | 694 + .../grid/api/core/handlers/EmptyHandler.kt | 12 + .../grid/api/core/handlers/ErrorHandler.kt | 80 + .../com/grid/api/core/handlers/JsonHandler.kt | 20 + .../grid/api/core/handlers/StringHandler.kt | 13 + .../kotlin/com/grid/api/core/http/Headers.kt | 111 + .../com/grid/api/core/http/HttpClient.kt | 17 + .../com/grid/api/core/http/HttpMethod.kt | 13 + .../com/grid/api/core/http/HttpRequest.kt | 175 + .../grid/api/core/http/HttpRequestBodies.kt | 122 + .../com/grid/api/core/http/HttpRequestBody.kt | 22 + .../com/grid/api/core/http/HttpResponse.kt | 19 + .../com/grid/api/core/http/HttpResponseFor.kt | 24 + .../http/PhantomReachableClosingHttpClient.kt | 25 + .../PhantomReachableClosingStreamResponse.kt | 20 + .../com/grid/api/core/http/QueryParams.kt | 125 + .../grid/api/core/http/RetryingHttpClient.kt | 248 + .../com/grid/api/core/http/StreamResponse.kt | 13 + .../grid/api/errors/BadRequestException.kt | 74 + .../com/grid/api/errors/GridException.kt | 4 + .../api/errors/GridInvalidDataException.kt | 4 + .../com/grid/api/errors/GridIoException.kt | 4 + .../grid/api/errors/GridRetryableException.kt | 13 + .../grid/api/errors/GridServiceException.kt | 16 + .../api/errors/InternalServerException.kt | 85 + .../com/grid/api/errors/NotFoundException.kt | 70 + .../api/errors/PermissionDeniedException.kt | 74 + .../com/grid/api/errors/RateLimitException.kt | 74 + .../grid/api/errors/UnauthorizedException.kt | 74 + .../errors/UnexpectedStatusCodeException.kt | 86 + .../errors/UnprocessableEntityException.kt | 74 + .../api/models/config/ConfigRetrieveParams.kt | 169 + .../api/models/config/ConfigUpdateParams.kt | 555 + .../models/config/CustomerInfoFieldName.kt | 219 + .../grid/api/models/config/PlatformConfig.kt | 493 + .../models/config/PlatformCurrencyConfig.kt | 570 + .../com/grid/api/models/customers/Customer.kt | 588 + .../api/models/customers/CustomerCreate.kt | 230 + .../models/customers/CustomerCreateParams.kt | 3692 ++++ .../models/customers/CustomerDeleteParams.kt | 222 + .../customers/CustomerDeleteResponse.kt | 3753 ++++ .../customers/CustomerGetKycLinkParams.kt | 217 + .../customers/CustomerGetKycLinkResponse.kt | 242 + .../CustomerListInternalAccountsPage.kt | 148 + .../CustomerListInternalAccountsPageAsync.kt | 149 + ...ustomerListInternalAccountsPageResponse.kt | 306 + .../CustomerListInternalAccountsParams.kt | 245 + .../api/models/customers/CustomerListPage.kt | 142 + .../models/customers/CustomerListPageAsync.kt | 142 + .../customers/CustomerListPageResponse.kt | 308 + .../models/customers/CustomerListParams.kt | 479 + .../api/models/customers/CustomerOneOf.kt | 3749 ++++ .../customers/CustomerRetrieveParams.kt | 188 + .../customers/CustomerRetrieveResponse.kt | 3753 ++++ .../api/models/customers/CustomerUpdate.kt | 159 + .../models/customers/CustomerUpdateParams.kt | 3537 ++++ .../customers/CustomerUpdateResponse.kt | 3753 ++++ .../customers/bulk/BulkGetJobStatusParams.kt | 192 + .../bulk/BulkGetJobStatusResponse.kt | 1126 ++ .../customers/bulk/BulkUploadCsvParams.kt | 474 + .../customers/bulk/BulkUploadCsvResponse.kt | 330 + .../externalaccounts/BeneficiaryOneOf.kt | 1556 ++ .../externalaccounts/ExternalAccount.kt | 729 + .../externalaccounts/ExternalAccountCreate.kt | 522 + .../ExternalAccountCreateParams.kt | 215 + .../ExternalAccountInfoOneOf.kt | 5532 ++++++ .../ExternalAccountListPage.kt | 142 + .../ExternalAccountListPageAsync.kt | 142 + .../ExternalAccountListPageResponse.kt | 304 + .../ExternalAccountListParams.kt | 242 + .../exchangerates/ExchangeRateListParams.kt | 260 + .../exchangerates/ExchangeRateListResponse.kt | 873 + .../api/models/invitations/CurrencyAmount.kt | 209 + .../invitations/InvitationCancelParams.kt | 238 + .../invitations/InvitationClaimParams.kt | 443 + .../invitations/InvitationCreateParams.kt | 656 + .../invitations/InvitationRetrieveParams.kt | 188 + .../api/models/invitations/UmaInvitation.kt | 698 + .../plaid/PlaidCreateLinkTokenParams.kt | 433 + .../plaid/PlaidCreateLinkTokenResponse.kt | 308 + .../plaid/PlaidSubmitPublicTokenParams.kt | 532 + .../PlatformListInternalAccountsParams.kt | 201 + .../PlatformListInternalAccountsResponse.kt | 196 + .../ExternalAccountCreateParams.kt | 216 + .../ExternalAccountListParams.kt | 197 + .../ExternalAccountListResponse.kt | 192 + .../grid/api/models/quotes/BaseDestination.kt | 112 + .../models/quotes/BasePaymentAccountInfo.kt | 113 + .../grid/api/models/quotes/BaseQuoteSource.kt | 112 + .../com/grid/api/models/quotes/Currency.kt | 260 + .../api/models/quotes/OutgoingRateDetails.kt | 450 + .../api/models/quotes/PaymentInstructions.kt | 6145 ++++++ .../com/grid/api/models/quotes/Quote.kt | 1132 ++ .../api/models/quotes/QuoteCreateParams.kt | 1532 ++ .../models/quotes/QuoteDestinationOneOf.kt | 1481 ++ .../api/models/quotes/QuoteExecuteParams.kt | 230 + .../grid/api/models/quotes/QuoteListPage.kt | 141 + .../api/models/quotes/QuoteListPageAsync.kt | 142 + .../models/quotes/QuoteListPageResponse.kt | 299 + .../grid/api/models/quotes/QuoteListParams.kt | 484 + .../api/models/quotes/QuoteRetrieveParams.kt | 186 + .../api/models/quotes/QuoteSourceOneOf.kt | 991 + .../receiver/CounterpartyFieldDefinition.kt | 216 + .../api/models/receiver/LookupResponse.kt | 584 + .../ReceiverLookupExternalAccountParams.kt | 235 + .../ReceiverLookupExternalAccountResponse.kt | 358 + .../receiver/ReceiverLookupUmaParams.kt | 230 + .../receiver/ReceiverLookupUmaResponse.kt | 366 + .../models/sandbox/SandboxSendFundsParams.kt | 578 + .../sandbox/SandboxSendFundsResponse.kt | 1601 ++ .../internalaccounts/InternalAccount.kt | 417 + .../InternalAccountFundParams.kt | 441 + .../sandbox/uma/UmaReceivePaymentParams.kt | 757 + .../com/grid/api/models/tokens/ApiToken.kt | 447 + .../com/grid/api/models/tokens/Permission.kt | 141 + .../api/models/tokens/TokenCreateParams.kt | 513 + .../api/models/tokens/TokenDeleteParams.kt | 222 + .../grid/api/models/tokens/TokenListPage.kt | 141 + .../api/models/tokens/TokenListPageAsync.kt | 142 + .../models/tokens/TokenListPageResponse.kt | 299 + .../grid/api/models/tokens/TokenListParams.kt | 296 + .../api/models/tokens/TokenRetrieveParams.kt | 183 + .../transactions/BaseTransactionSource.kt | 154 + .../transactions/IncomingTransaction.kt | 1604 ++ .../transactions/TransactionApproveParams.kt | 534 + .../transactions/TransactionListPage.kt | 143 + .../transactions/TransactionListPageAsync.kt | 143 + .../TransactionListPageResponse.kt | 300 + .../transactions/TransactionListParams.kt | 489 + .../transactions/TransactionRejectParams.kt | 411 + .../transactions/TransactionRetrieveParams.kt | 190 + .../transactions/TransactionSourceOneOf.kt | 948 + .../models/transactions/TransactionStatus.kt | 171 + .../models/transactions/TransactionType.kt | 134 + .../transferin/BaseTransactionDestination.kt | 156 + .../grid/api/models/transferin/Transaction.kt | 1718 ++ .../transferin/TransferInCreateParams.kt | 900 + .../transferout/TransferOutCreateParams.kt | 896 + .../umaproviders/UmaProviderListPage.kt | 142 + .../umaproviders/UmaProviderListPageAsync.kt | 142 + .../UmaProviderListPageResponse.kt | 288 + .../umaproviders/UmaProviderListParams.kt | 419 + .../umaproviders/UmaProviderListResponse.kt | 437 + .../models/webhooks/WebhookSendTestParams.kt | 207 + .../webhooks/WebhookSendTestResponse.kt | 257 + .../api/services/async/ConfigServiceAsync.kt | 91 + .../services/async/ConfigServiceAsyncImpl.kt | 116 + .../services/async/CustomerServiceAsync.kt | 286 + .../async/CustomerServiceAsyncImpl.kt | 352 + .../async/ExchangeRateServiceAsync.kt | 77 + .../async/ExchangeRateServiceAsyncImpl.kt | 79 + .../services/async/InvitationServiceAsync.kt | 203 + .../async/InvitationServiceAsyncImpl.kt | 198 + .../api/services/async/PlaidServiceAsync.kt | 114 + .../services/async/PlaidServiceAsyncImpl.kt | 122 + .../services/async/PlatformServiceAsync.kt | 81 + .../async/PlatformServiceAsyncImpl.kt | 94 + .../api/services/async/QuoteServiceAsync.kt | 215 + .../services/async/QuoteServiceAsyncImpl.kt | 197 + .../services/async/ReceiverServiceAsync.kt | 154 + .../async/ReceiverServiceAsyncImpl.kt | 122 + .../api/services/async/SandboxServiceAsync.kt | 69 + .../services/async/SandboxServiceAsyncImpl.kt | 108 + .../api/services/async/TokenServiceAsync.kt | 170 + .../services/async/TokenServiceAsyncImpl.kt | 195 + .../services/async/TransactionServiceAsync.kt | 220 + .../async/TransactionServiceAsyncImpl.kt | 207 + .../services/async/TransferInServiceAsync.kt | 61 + .../async/TransferInServiceAsyncImpl.kt | 81 + .../services/async/TransferOutServiceAsync.kt | 57 + .../async/TransferOutServiceAsyncImpl.kt | 81 + .../services/async/UmaProviderServiceAsync.kt | 74 + .../async/UmaProviderServiceAsyncImpl.kt | 87 + .../api/services/async/WebhookServiceAsync.kt | 67 + .../services/async/WebhookServiceAsyncImpl.kt | 81 + .../async/customers/BulkServiceAsync.kt | 161 + .../async/customers/BulkServiceAsyncImpl.kt | 121 + .../customers/ExternalAccountServiceAsync.kt | 131 + .../ExternalAccountServiceAsyncImpl.kt | 127 + .../platform/ExternalAccountServiceAsync.kt | 130 + .../ExternalAccountServiceAsyncImpl.kt | 119 + .../sandbox/InternalAccountServiceAsync.kt | 78 + .../InternalAccountServiceAsyncImpl.kt | 87 + .../services/async/sandbox/UmaServiceAsync.kt | 56 + .../async/sandbox/UmaServiceAsyncImpl.kt | 81 + .../api/services/blocking/ConfigService.kt | 87 + .../services/blocking/ConfigServiceImpl.kt | 114 + .../api/services/blocking/CustomerService.kt | 277 + .../services/blocking/CustomerServiceImpl.kt | 351 + .../services/blocking/ExchangeRateService.kt | 74 + .../blocking/ExchangeRateServiceImpl.kt | 79 + .../services/blocking/InvitationService.kt | 200 + .../blocking/InvitationServiceImpl.kt | 198 + .../api/services/blocking/PlaidService.kt | 112 + .../api/services/blocking/PlaidServiceImpl.kt | 120 + .../api/services/blocking/PlatformService.kt | 75 + .../services/blocking/PlatformServiceImpl.kt | 93 + .../api/services/blocking/QuoteService.kt | 209 + .../api/services/blocking/QuoteServiceImpl.kt | 186 + .../api/services/blocking/ReceiverService.kt | 150 + .../services/blocking/ReceiverServiceImpl.kt | 122 + .../api/services/blocking/SandboxService.kt | 65 + .../services/blocking/SandboxServiceImpl.kt | 107 + .../api/services/blocking/TokenService.kt | 163 + .../api/services/blocking/TokenServiceImpl.kt | 184 + .../services/blocking/TransactionService.kt | 215 + .../blocking/TransactionServiceImpl.kt | 207 + .../services/blocking/TransferInService.kt | 58 + .../blocking/TransferInServiceImpl.kt | 81 + .../services/blocking/TransferOutService.kt | 56 + .../blocking/TransferOutServiceImpl.kt | 81 + .../services/blocking/UmaProviderService.kt | 71 + .../blocking/UmaProviderServiceImpl.kt | 87 + .../api/services/blocking/WebhookService.kt | 61 + .../services/blocking/WebhookServiceImpl.kt | 81 + .../blocking/customers/BulkService.kt | 159 + .../blocking/customers/BulkServiceImpl.kt | 118 + .../customers/ExternalAccountService.kt | 129 + .../customers/ExternalAccountServiceImpl.kt | 125 + .../platform/ExternalAccountService.kt | 128 + .../platform/ExternalAccountServiceImpl.kt | 117 + .../sandbox/InternalAccountService.kt | 78 + .../sandbox/InternalAccountServiceImpl.kt | 85 + .../services/blocking/sandbox/UmaService.kt | 56 + .../blocking/sandbox/UmaServiceImpl.kt | 78 + .../META-INF/proguard/grid-kotlin-core.pro | 32 + .../com/grid/api/TestServerExtension.kt | 62 + .../com/grid/api/core/AutoPagerAsyncTest.kt | 37 + .../kotlin/com/grid/api/core/AutoPagerTest.kt | 31 + .../com/grid/api/core/ClientOptionsTest.kt | 38 + .../com/grid/api/core/ObjectMappersTest.kt | 117 + .../com/grid/api/core/PhantomReachableTest.kt | 27 + .../kotlin/com/grid/api/core/UtilsTest.kt | 33 + .../kotlin/com/grid/api/core/ValuesTest.kt | 127 + .../com/grid/api/core/http/HeadersTest.kt | 242 + .../com/grid/api/core/http/HttpRequestTest.kt | 110 + .../com/grid/api/core/http/QueryParamsTest.kt | 180 + .../api/core/http/RetryingHttpClientTest.kt | 349 + .../models/config/ConfigRetrieveParamsTest.kt | 13 + .../models/config/ConfigUpdateParamsTest.kt | 136 + .../api/models/config/PlatformConfigTest.kt | 161 + .../config/PlatformCurrencyConfigTest.kt | 118 + .../customers/CustomerCreateParamsTest.kt | 121 + .../models/customers/CustomerCreateTest.kt | 41 + .../customers/CustomerDeleteParamsTest.kt | 23 + .../customers/CustomerDeleteResponseTest.kt | 239 + .../customers/CustomerGetKycLinkParamsTest.kt | 50 + .../CustomerGetKycLinkResponseTest.kt | 46 + ...merListInternalAccountsPageResponseTest.kt | 170 + .../CustomerListInternalAccountsParamsTest.kt | 52 + .../customers/CustomerListPageResponseTest.kt | 124 + .../customers/CustomerListParamsTest.kt | 71 + .../api/models/customers/CustomerOneOfTest.kt | 238 + .../customers/CustomerRetrieveParamsTest.kt | 23 + .../customers/CustomerRetrieveResponseTest.kt | 240 + .../grid/api/models/customers/CustomerTest.kt | 57 + .../customers/CustomerUpdateParamsTest.kt | 147 + .../customers/CustomerUpdateResponseTest.kt | 239 + .../models/customers/CustomerUpdateTest.kt | 34 + .../bulk/BulkGetJobStatusParamsTest.kt | 23 + .../bulk/BulkGetJobStatusResponseTest.kt | 111 + .../customers/bulk/BulkUploadCsvParamsTest.kt | 38 + .../bulk/BulkUploadCsvResponseTest.kt | 42 + .../externalaccounts/BeneficiaryOneOfTest.kt | 147 + .../ExternalAccountCreateParamsTest.kt | 185 + .../ExternalAccountCreateTest.kt | 133 + .../ExternalAccountInfoOneOfTest.kt | 1155 ++ .../ExternalAccountListPageResponseTest.kt | 158 + .../ExternalAccountListParamsTest.kt | 52 + .../externalaccounts/ExternalAccountTest.kt | 140 + .../ExchangeRateListParamsTest.kt | 49 + .../ExchangeRateListResponseTest.kt | 119 + .../models/invitations/CurrencyAmountTest.kt | 64 + .../invitations/InvitationCancelParamsTest.kt | 23 + .../invitations/InvitationClaimParamsTest.kt | 43 + .../invitations/InvitationCreateParamsTest.kt | 47 + .../InvitationRetrieveParamsTest.kt | 23 + .../models/invitations/UmaInvitationTest.kt | 107 + .../plaid/PlaidCreateLinkTokenParamsTest.kt | 28 + .../plaid/PlaidCreateLinkTokenResponseTest.kt | 57 + .../plaid/PlaidSubmitPublicTokenParamsTest.kt | 61 + .../PlatformListInternalAccountsParamsTest.kt | 33 + ...latformListInternalAccountsResponseTest.kt | 161 + .../ExternalAccountCreateParamsTest.kt | 188 + .../ExternalAccountListParamsTest.kt | 33 + .../ExternalAccountListResponseTest.kt | 151 + .../api/models/quotes/BaseDestinationTest.kt | 30 + .../quotes/BasePaymentAccountInfoTest.kt | 30 + .../api/models/quotes/BaseQuoteSourceTest.kt | 30 + .../grid/api/models/quotes/CurrencyTest.kt | 47 + .../models/quotes/OutgoingRateDetailsTest.kt | 53 + .../models/quotes/PaymentInstructionsTest.kt | 78 + .../models/quotes/QuoteCreateParamsTest.kt | 132 + .../quotes/QuoteDestinationOneOfTest.kt | 234 + .../models/quotes/QuoteExecuteParamsTest.kt | 26 + .../quotes/QuoteListPageResponseTest.kt | 302 + .../api/models/quotes/QuoteListParamsTest.kt | 71 + .../models/quotes/QuoteRetrieveParamsTest.kt | 23 + .../api/models/quotes/QuoteSourceOneOfTest.kt | 106 + .../com/grid/api/models/quotes/QuoteTest.kt | 281 + .../CounterpartyFieldDefinitionTest.kt | 42 + .../api/models/receiver/LookupResponseTest.kt | 106 + ...ReceiverLookupExternalAccountParamsTest.kt | 64 + ...ceiverLookupExternalAccountResponseTest.kt | 111 + .../receiver/ReceiverLookupUmaParamsTest.kt | 59 + .../receiver/ReceiverLookupUmaResponseTest.kt | 110 + .../sandbox/SandboxSendFundsParamsTest.kt | 48 + .../sandbox/SandboxSendFundsResponseTest.kt | 399 + .../InternalAccountFundParamsTest.kt | 44 + .../internalaccounts/InternalAccountTest.kt | 147 + .../uma/UmaReceivePaymentParamsTest.kt | 56 + .../grid/api/models/tokens/ApiTokenTest.kt | 57 + .../models/tokens/TokenCreateParamsTest.kt | 28 + .../models/tokens/TokenDeleteParamsTest.kt | 23 + .../tokens/TokenListPageResponseTest.kt | 79 + .../api/models/tokens/TokenListParamsTest.kt | 62 + .../models/tokens/TokenRetrieveParamsTest.kt | 23 + .../transactions/BaseTransactionSourceTest.kt | 32 + .../transactions/IncomingTransactionTest.kt | 227 + .../TransactionApproveParamsTest.kt | 60 + .../TransactionListPageResponseTest.kt | 135 + .../transactions/TransactionListParamsTest.kt | 77 + .../TransactionRejectParamsTest.kt | 46 + .../TransactionRetrieveParamsTest.kt | 23 + .../TransactionSourceOneOfTest.kt | 106 + .../BaseTransactionDestinationTest.kt | 34 + .../api/models/transferin/TransactionTest.kt | 113 + .../transferin/TransferInCreateParamsTest.kt | 144 + .../TransferOutCreateParamsTest.kt | 144 + .../UmaProviderListPageResponseTest.kt | 100 + .../umaproviders/UmaProviderListParamsTest.kt | 58 + .../UmaProviderListResponseTest.kt | 79 + .../webhooks/WebhookSendTestParamsTest.kt | 13 + .../webhooks/WebhookSendTestResponseTest.kt | 45 + .../grid/api/services/ErrorHandlingTest.kt | 777 + .../grid/api/services/ServiceParamsTest.kt | 81 + .../services/async/ConfigServiceAsyncTest.kt | 89 + .../async/CustomerServiceAsyncTest.kt | 191 + .../async/ExchangeRateServiceAsyncTest.kt | 37 + .../async/InvitationServiceAsyncTest.kt | 94 + .../services/async/PlaidServiceAsyncTest.kt | 59 + .../async/PlatformServiceAsyncTest.kt | 33 + .../services/async/QuoteServiceAsyncTest.kt | 103 + .../async/ReceiverServiceAsyncTest.kt | 61 + .../services/async/SandboxServiceAsyncTest.kt | 37 + .../services/async/TokenServiceAsyncTest.kt | 83 + .../async/TransactionServiceAsyncTest.kt | 96 + .../async/TransferInServiceAsyncTest.kt | 46 + .../async/TransferOutServiceAsyncTest.kt | 46 + .../async/UmaProviderServiceAsyncTest.kt | 29 + .../services/async/WebhookServiceAsyncTest.kt | 29 + .../async/customers/BulkServiceAsyncTest.kt | 49 + .../ExternalAccountServiceAsyncTest.kt | 84 + .../ExternalAccountServiceAsyncTest.kt | 88 + .../InternalAccountServiceAsyncTest.kt | 36 + .../async/sandbox/UmaServiceAsyncTest.kt | 39 + .../services/blocking/ConfigServiceTest.kt | 89 + .../services/blocking/CustomerServiceTest.kt | 191 + .../blocking/ExchangeRateServiceTest.kt | 37 + .../blocking/InvitationServiceTest.kt | 94 + .../api/services/blocking/PlaidServiceTest.kt | 59 + .../services/blocking/PlatformServiceTest.kt | 33 + .../api/services/blocking/QuoteServiceTest.kt | 103 + .../services/blocking/ReceiverServiceTest.kt | 61 + .../services/blocking/SandboxServiceTest.kt | 37 + .../api/services/blocking/TokenServiceTest.kt | 83 + .../blocking/TransactionServiceTest.kt | 96 + .../blocking/TransferInServiceTest.kt | 46 + .../blocking/TransferOutServiceTest.kt | 46 + .../blocking/UmaProviderServiceTest.kt | 29 + .../services/blocking/WebhookServiceTest.kt | 29 + .../blocking/customers/BulkServiceTest.kt | 49 + .../customers/ExternalAccountServiceTest.kt | 84 + .../platform/ExternalAccountServiceTest.kt | 88 + .../sandbox/InternalAccountServiceTest.kt | 36 + .../blocking/sandbox/UmaServiceTest.kt | 39 + grid-kotlin-example/build.gradle.kts | 20 + grid-kotlin-lib/.keep | 4 + grid-kotlin-proguard-test/build.gradle.kts | 101 + .../api/proguard/ProGuardCompatibilityTest.kt | 123 + grid-kotlin-proguard-test/test.pro | 9 + grid-kotlin/build.gradle.kts | 29 + mintlify/AGENTS.md | 396 - mintlify/CLAUDE.md | 46 - mintlify/README.md | 23 - mintlify/api-reference/authentication.mdx | 13 - mintlify/api-reference/environments.mdx | 25 - mintlify/api-reference/terminology.mdx | 8 - mintlify/developer-resources/samples.mdx | 13 - mintlify/docs.json | 312 - mintlify/favicon.svg | 3 - .../SuisseIntlMono-Bold-WebXL.woff2 | Bin 17300 -> 0 bytes .../SuisseIntlMono-Regular-WebXL.woff2 | Bin 17284 -> 0 bytes .../fonts/suisse-intl/SuisseIntl-Book.woff2 | Bin 65220 -> 0 bytes .../fonts/suisse-intl/SuisseIntl-Medium.woff2 | Bin 51256 -> 0 bytes .../suisse-intl/SuisseIntl-Regular.woff2 | Bin 51596 -> 0 bytes mintlify/global-p2p/country-support.mdx | 7 - .../implementation-overview.mdx | 82 - .../platform-configuration.mdx | 203 - mintlify/global-p2p/index.mdx | 60 - .../managing-accounts/external-accounts.mdx | 10 - .../managing-accounts/internal-accounts.mdx | 10 - .../global-p2p/managing-accounts/plaid.mdx | 10 - .../configuring-customers.mdx | 281 - .../onboarding-customers/invitations.mdx | 233 - .../platform-tools/postman-collection.mdx | 11 - .../platform-tools/sandbox-testing.mdx | 224 - .../platform-tools/uma-test-wallet.mdx | 12 - .../global-p2p/platform-tools/webhooks.mdx | 10 - mintlify/global-p2p/quickstart.mdx | 214 - .../depositing-funds.mdx | 10 - .../error-handling.mdx | 10 - .../receiving-payments.mdx | 202 - .../reconciliation.mdx | 7 - .../sending-payments.mdx | 28 - mintlify/global-p2p/terminology.mdx | 10 - mintlify/images/IconArrowTopBottom.svg | 3 - mintlify/images/IconChevronDoubleUp.svg | 3 - mintlify/images/IconGift.svg | 3 - mintlify/images/IconGlobus-green.svg | 3 - mintlify/images/IconGlobus.svg | 3 - mintlify/images/IconHammer-white.svg | 4 - mintlify/images/IconHammer.svg | 4 - mintlify/images/blue-lines-sm.svg | 6 - mintlify/images/blue-lines.svg | 6 - mintlify/images/entities.png | Bin 121708 -> 0 bytes mintlify/images/github-white.svg | 3 - mintlify/images/github.svg | 1 - mintlify/images/grid-white.svg | 3 - mintlify/images/grid.svg | 3 - mintlify/images/linkedin.svg | 1 - mintlify/images/postman-white.svg | 23 - mintlify/images/postman.svg | 23 - mintlify/images/x-twitter.svg | 1 - mintlify/index.mdx | 189 - mintlify/logo/dark.svg | 21 - mintlify/logo/light.svg | 21 - mintlify/openapi.yaml | 6897 ------- .../depositing-funds/depositing-funds.mdx | 8 - .../depositing-funds/external-accounts.mdx | 28 - .../depositing-funds/internal-accounts.mdx | 28 - .../depositing-funds/plaid.mdx | 32 - mintlify/payouts-and-b2b/index.mdx | 67 - .../onboarding/configuring-customers.mdx | 345 - .../onboarding/implementation-overview.mdx | 76 - .../onboarding/platform-configuration.mdx | 7 - .../payment-flow/error-handling.mdx | 8 - .../payment-flow/list-transactions.mdx | 361 - .../payment-flow/reconciliation.mdx | 7 - .../payment-flow/send-payment.mdx | 582 - .../platform-tools/postman-collection.mdx | 9 - .../platform-tools/sandbox-testing.mdx | 305 - .../platform-tools/webhooks.mdx | 7 - mintlify/payouts-and-b2b/quickstart.mdx | 412 - mintlify/payouts-and-b2b/terminology.mdx | 8 - .../core-concepts/account-model.mdx | 289 - .../core-concepts/currencies-and-rails.mdx | 80 - .../core-concepts/entities.mdx | 130 - .../core-concepts/quote-system.mdx | 423 - .../core-concepts/transaction-lifecycle.mdx | 334 - .../platform-overview/introduction/faq.mdx | 38 - .../introduction/platform-capabilities.mdx | 230 - .../introduction/what-is-grid.mdx | 90 - mintlify/ramps/accounts/depositing-funds.mdx | 8 - mintlify/ramps/accounts/external-accounts.mdx | 354 - mintlify/ramps/accounts/internal-accounts.mdx | 256 - mintlify/ramps/accounts/plaid.mdx | 400 - .../fiat-crypto-conversion.mdx | 290 - .../conversion-flows/self-custody-wallets.mdx | 432 - mintlify/ramps/index.mdx | 108 - .../onboarding/configuring-customers.mdx | 106 - .../onboarding/implementation-overview.mdx | 155 - .../onboarding/platform-configuration.mdx | 189 - .../platform-tools/postman-collection.mdx | 9 - .../ramps/platform-tools/sandbox-testing.mdx | 411 - mintlify/ramps/platform-tools/webhooks.mdx | 394 - mintlify/ramps/quickstart.mdx | 280 - mintlify/ramps/terminology.mdx | 10 - mintlify/ramps/webhooks.mdx | 7 - .../configuring-customers.mdx | 153 - .../developer-guides/distributing-rewards.mdx | 360 - .../developer-guides/external-accounts.mdx | 28 - .../implementation-overview.mdx | 92 - .../developer-guides/internal-accounts.mdx | 28 - .../developer-guides/listing-transactions.mdx | 277 - mintlify/rewards/developer-guides/plaid.mdx | 28 - .../platform-configuration.mdx | 7 - mintlify/rewards/index.mdx | 86 - .../platform-tools/postman-collection.mdx | 9 - .../platform-tools/sandbox-testing.mdx | 342 - mintlify/rewards/platform-tools/webhooks.mdx | 7 - mintlify/rewards/quickstart.mdx | 323 - mintlify/rewards/terminology.mdx | 10 - mintlify/snippets/country-support.mdx | 99 - .../creating-customers/customer-types.mdx | 5 - .../snippets/creating-customers/customers.mdx | 303 - .../creating-customers/onboarding-model.mdx | 6 - mintlify/snippets/depositing-funds.mdx | 86 - mintlify/snippets/error-handling.mdx | 551 - mintlify/snippets/external-accounts.mdx | 354 - mintlify/snippets/internal-accounts.mdx | 355 - mintlify/snippets/kyc/kyc-regulated.mdx | 83 - mintlify/snippets/kyc/kyc-unregulated.mdx | 73 - mintlify/snippets/kyc/kyc-webhooks.mdx | 92 - mintlify/snippets/payment-flow.mdx | 12 - mintlify/snippets/plaid-integration.mdx | 318 - .../platform-config-currency-api-webhooks.mdx | 57 - .../platform-configuration-non-uma.mdx | 115 - .../snippets/platform-configuration-uma.mdx | 195 - mintlify/snippets/postman-collection.mdx | 9 - .../reconciliation/reconciliation.mdx | 130 - mintlify/snippets/sending/cross-currency.mdx | 232 - mintlify/snippets/sending/same-currency.mdx | 96 - mintlify/snippets/sending/uma.mdx | 97 - mintlify/snippets/terminology.mdx | 135 - mintlify/snippets/uma-test-wallet.mdx | 20 - mintlify/snippets/variables.mdx | 6 - mintlify/snippets/webhooks.mdx | 205 - mintlify/styles/fonts.css | 61 - mintlify/styles/styles.css | 342 - mintlify/styles/tokens.css | 90 - openapi.yaml | 6897 ------- openapi/README.md | 467 - .../components/schemas/common/Address.yaml | 30 - .../common/BasePaymentAccountInfo.yaml | 21 - .../schemas/common/BaseWalletInfo.yaml | 13 - .../schemas/common/CadAccountInfo.yaml | 32 - .../schemas/common/ClabeAccountInfo.yaml | 16 - .../common/CounterpartyFieldDefinition.yaml | 11 - .../components/schemas/common/Currency.yaml | 22 - .../schemas/common/CurrencyAmount.yaml | 14 - openapi/components/schemas/common/Error.yaml | 11 - .../schemas/common/GbpAccountInfo.yaml | 22 - .../components/schemas/common/GridError.yaml | 13 - .../schemas/common/IbanAccountInfo.yaml | 23 - .../schemas/common/NgnAccountInfo.yaml | 21 - .../schemas/common/PaymentAccountType.yaml | 16 - .../schemas/common/PaymentBaseWalletInfo.yaml | 10 - .../common/PaymentClabeAccountInfo.yaml | 13 - .../common/PaymentIbanAccountInfo.yaml | 13 - .../schemas/common/PaymentInstructions.yaml | 52 - .../common/PaymentLightningInvoiceInfo.yaml | 10 - .../schemas/common/PaymentNgnAccountInfo.yaml | 13 - .../schemas/common/PaymentPixAccountInfo.yaml | 3 - .../common/PaymentPolygonWalletInfo.yaml | 11 - .../common/PaymentSolanaWalletInfo.yaml | 11 - .../common/PaymentSparkWalletInfo.yaml | 17 - .../schemas/common/PaymentTronWalletInfo.yaml | 9 - .../schemas/common/PaymentUpiAccountInfo.yaml | 3 - .../schemas/common/PaymentUsAccountInfo.yaml | 13 - .../schemas/common/PhpAccountInfo.yaml | 18 - .../schemas/common/PixAccountInfo.yaml | 29 - .../schemas/common/PolygonWalletInfo.yaml | 13 - .../common/ReconciliationInstructions.yaml | 10 - openapi/components/schemas/common/Refund.yaml | 19 - .../schemas/common/SgdAccountInfo.yaml | 26 - .../schemas/common/SolanaWalletInfo.yaml | 13 - .../schemas/common/SparkWalletInfo.yaml | 13 - .../schemas/common/TronWalletInfo.yaml | 13 - .../schemas/common/UpiAccountInfo.yaml | 13 - .../schemas/common/UsAccountInfo.yaml | 33 - .../schemas/config/PlatformConfig.yaml | 46 - .../config/PlatformCurrencyConfig.yaml | 73 - .../BulkCustomerImportErrorEntry.yaml | 11 - .../customers/BulkCustomerImportJob.yaml | 55 - .../customers/BulkUploadWebhookRequest.yaml | 12 - .../schemas/customers/BusinessCustomer.yaml | 7 - .../BusinessCustomerCreateRequest.yaml | 7 - .../customers/BusinessCustomerFields.yaml | 16 - .../BusinessCustomerUpdateRequest.yaml | 3 - .../schemas/customers/BusinessInfo.yaml | 17 - .../schemas/customers/BusinessInfoUpdate.yaml | 15 - .../schemas/customers/Customer.yaml | 47 - .../customers/CustomerCreateRequest.yaml | 23 - .../customers/CustomerCreateRequestOneOf.yaml | 10 - .../customers/CustomerInfoFieldName.yaml | 22 - .../schemas/customers/CustomerOneOf.yaml | 10 - .../schemas/customers/CustomerType.yaml | 6 - .../customers/CustomerUpdateRequest.yaml | 17 - .../customers/CustomerUpdateRequestOneOf.yaml | 10 - .../schemas/customers/IndividualCustomer.yaml | 3 - .../IndividualCustomerCreateRequest.yaml | 3 - .../customers/IndividualCustomerFields.yaml | 23 - .../IndividualCustomerUpdateRequest.yaml | 3 - .../schemas/customers/InternalAccount.yaml | 34 - .../schemas/customers/KycStatus.yaml | 11 - .../customers/UltimateBeneficialOwner.yaml | 53 - .../components/schemas/errors/Error400.yaml | 78 - .../components/schemas/errors/Error401.yaml | 28 - .../components/schemas/errors/Error403.yaml | 32 - .../components/schemas/errors/Error404.yaml | 40 - .../components/schemas/errors/Error409.yaml | 28 - .../components/schemas/errors/Error410.yaml | 26 - .../components/schemas/errors/Error412.yaml | 25 - .../components/schemas/errors/Error424.yaml | 32 - .../components/schemas/errors/Error500.yaml | 28 - .../components/schemas/errors/Error501.yaml | 28 - .../external_accounts/BaseBeneficiary.yaml | 13 - .../BaseExternalAccountInfo.yaml | 25 - .../BaseWalletExternalAccountInfo.yaml | 4 - .../external_accounts/BeneficiaryOneOf.yaml | 10 - .../external_accounts/BeneficiaryType.yaml | 6 - .../BusinessBeneficiary.yaml | 23 - .../CadAccountExternalAccountInfo.yaml | 9 - .../ClabeAccountExternalAccountInfo.yaml | 9 - .../external_accounts/ExternalAccount.yaml | 38 - .../ExternalAccountCreateRequest.yaml | 32 - .../ExternalAccountInfoOneOf.yaml | 52 - .../ExternalAccountStatus.yaml | 7 - .../ExternalAccountType.yaml | 20 - .../GbpAccountExternalAccountInfo.yaml | 9 - .../IbanAccountExternalAccountInfo.yaml | 9 - .../IndividualBeneficiary.yaml | 26 - .../LightningExternalAccountInfo.yaml | 24 - .../NgnAccountExternalAccountInfo.yaml | 27 - .../PhpAccountExternalAccountInfo.yaml | 9 - .../PixAccountExternalAccountInfo.yaml | 9 - .../PolygonWalletExternalAccountInfo.yaml | 4 - .../SgdAccountExternalAccountInfo.yaml | 9 - .../SolanaWalletExternalAccountInfo.yaml | 4 - .../SparkWalletExternalAccountInfo.yaml | 4 - .../TronWalletExternalAccountInfo.yaml | 4 - .../UpiAccountExternalAccountInfo.yaml | 9 - .../UsAccountExternalAccountInfo.yaml | 9 - .../schemas/invitations/UmaInvitation.yaml | 68 - .../schemas/plaid/PlaidCallbackRequest.yaml | 16 - .../schemas/plaid/PlaidCallbackResponse.yaml | 12 - .../schemas/plaid/PlaidLinkTokenRequest.yaml | 8 - .../schemas/plaid/PlaidLinkTokenResponse.yaml | 30 - .../schemas/quotes/AccountDestination.yaml | 16 - .../schemas/quotes/AccountQuoteSource.yaml | 20 - .../schemas/quotes/BaseDestination.yaml | 12 - .../schemas/quotes/BaseQuoteSource.yaml | 11 - .../schemas/quotes/DestinationType.yaml | 7 - .../ExternalAccountDetailsDestination.yaml | 18 - openapi/components/schemas/quotes/Quote.yaml | 104 - .../schemas/quotes/QuoteDestinationOneOf.yaml | 13 - .../schemas/quotes/QuoteLockSide.yaml | 10 - .../schemas/quotes/QuoteRequest.yaml | 65 - .../schemas/quotes/QuoteSourceOneOf.yaml | 10 - .../schemas/quotes/QuoteSourceType.yaml | 6 - .../quotes/RealtimeFundingQuoteSource.yaml | 29 - .../schemas/quotes/UmaAddressDestination.yaml | 33 - .../schemas/receiver/CurrencyPreference.yaml | 29 - .../receiver/ReceiverLookupResponse.yaml | 23 - .../components/schemas/tokens/ApiToken.yaml | 45 - .../components/schemas/tokens/Permission.yaml | 10 - .../AccountTransactionDestination.yaml | 16 - .../AccountTransactionSource.yaml | 16 - .../BaseTransactionDestination.yaml | 15 - .../transactions/BaseTransactionSource.yaml | 15 - .../transactions/IncomingRateDetails.yaml | 41 - .../transactions/IncomingTransaction.yaml | 20 - .../IncomingTransactionFailureReason.yaml | 14 - .../transactions/OutgoingRateDetails.yaml | 59 - .../transactions/OutgoingTransaction.yaml | 61 - .../OutgoingTransactionFailureReason.yaml | 12 - .../schemas/transactions/Transaction.yaml | 61 - .../TransactionDestinationOneOf.yaml | 10 - .../TransactionDestinationType.yaml | 6 - .../transactions/TransactionSourceOneOf.yaml | 10 - .../transactions/TransactionSourceType.yaml | 7 - .../transactions/TransactionStatus.yaml | 11 - .../schemas/transactions/TransactionType.yaml | 5 - .../UmaAddressTransactionDestination.yaml | 16 - .../UmaAddressTransactionSource.yaml | 16 - .../schemas/uma_providers/UmaProvider.yaml | 39 - .../webhooks/AccountStatusWebhook.yaml | 21 - .../schemas/webhooks/BaseWebhook.yaml | 31 - .../webhooks/IncomingPaymentWebhook.yaml | 21 - ...comingPaymentWebhookForbiddenResponse.yaml | 11 - .../IncomingPaymentWebhookResponse.yaml | 9 - ...ngPaymentWebhookUnprocessableResponse.yaml | 14 - .../webhooks/InvitationClaimedWebhook.yaml | 13 - .../schemas/webhooks/KycStatusWebhook.yaml | 13 - .../webhooks/OutgoingPaymentWebhook.yaml | 12 - .../schemas/webhooks/TestWebhookRequest.yaml | 8 - .../schemas/webhooks/TestWebhookResponse.yaml | 18 - .../schemas/webhooks/WebhookType.yaml | 12 - openapi/openapi.yaml | 169 - openapi/paths/account/account.yaml | 86 - openapi/paths/customers/customers.yaml | 181 - .../paths/customers/customers_bulk_csv.yaml | 146 - .../customers_bulk_jobs_{jobId}.yaml | 55 - .../customers_external_accounts.yaml | 182 - .../customers_internal_accounts.yaml | 88 - .../paths/customers/customers_kyc_link.yaml | 54 - .../customers/customers_{customerId}.yaml | 178 - .../internal-accounts/internal_accounts.yaml | 88 - openapi/paths/invitations/invitations.yaml | 75 - .../invitations_{invitationCode}.yaml | 41 - .../invitations_{invitationCode}_cancel.yaml | 71 - .../invitations_{invitationCode}_claim.yaml | 74 - .../plaid_callback_{plaid_link_token}.yaml | 64 - openapi/paths/plaid/plaid_link-tokens.yaml | 58 - openapi/paths/platform/config.yaml | 100 - .../platform/platform_external_accounts.yaml | 143 - .../platform/platform_internal_accounts.yaml | 51 - openapi/paths/quotes/quotes.yaml | 256 - openapi/paths/quotes/quotes_{quoteId}.yaml | 46 - .../quotes/quotes_{quoteId}_execute.yaml | 65 - ...receiver_external-account_{accountId}.yaml | 86 - .../receiver_uma_{receiverUmaAddress}.yaml | 84 - ...ox_internal_accounts_{accountId}_fund.yaml | 87 - openapi/paths/sandbox/sandbox_send.yaml | 75 - .../paths/sandbox/sandbox_uma_receive.yaml | 87 - openapi/paths/tokens/tokens.yaml | 163 - openapi/paths/tokens/tokens_{tokenId}.yaml | 75 - openapi/paths/transactions/transactions.yaml | 140 - .../transactions_{transactionId}.yaml | 40 - .../transactions_{transactionId}_approve.yaml | 73 - .../transactions_{transactionId}_reject.yaml | 72 - openapi/paths/transfers/transfer_in.yaml | 98 - openapi/paths/transfers/transfer_out.yaml | 96 - .../paths/uma_providers/uma_providers.yaml | 86 - openapi/paths/webhooks/webhooks_test.yaml | 33 - openapi/webhooks/account-status.yaml | 92 - openapi/webhooks/bulk-upload.yaml | 100 - openapi/webhooks/incoming-payment.yaml | 189 - openapi/webhooks/invitation-claimed.yaml | 94 - openapi/webhooks/kyc-status.yaml | 85 - openapi/webhooks/outgoing-payment.yaml | 143 - openapi/webhooks/test-webhook.yaml | 73 - package-lock.json | 15499 ---------------- package.json | 27 - scripts/build | 8 + scripts/detect-breaking-changes | 22 + scripts/format | 13 + scripts/lint | 13 + scripts/mock | 41 + scripts/test | 56 + settings.gradle.kts | 14 + 819 files changed, 111051 insertions(+), 58796 deletions(-) delete mode 100644 .claude/skills/grid-api/SKILL.md delete mode 100644 .claude/skills/grid-api/references/account-types.md delete mode 100644 .claude/skills/grid-api/references/endpoints.md delete mode 100644 .claude/skills/grid-api/references/workflows.md delete mode 100644 .cursor/commands/transfer-in.md create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json delete mode 100644 .fossa.yml delete mode 100644 .gitattributes create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/detect-breaking-changes.yml delete mode 100644 .github/workflows/docs-sync.yml delete mode 100644 .github/workflows/dummy.yml delete mode 100644 .github/workflows/lint.yml delete mode 100644 .github/workflows/openapi-build.yml delete mode 100644 .github/workflows/stainless-action.yml delete mode 100644 .markdownlint.json delete mode 100644 .redocly.lint-ignore.yaml delete mode 100644 .redocly.yaml delete mode 100644 .stainless/stainless.yml delete mode 100644 .stainless/workspace.json create mode 100644 .stats.yml delete mode 100644 CLAUDE.md delete mode 100644 Makefile create mode 100644 SECURITY.md create mode 100644 build.gradle.kts create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/grid.java.gradle.kts create mode 100644 buildSrc/src/main/kotlin/grid.kotlin.gradle.kts create mode 100644 buildSrc/src/main/kotlin/grid.publish.gradle.kts delete mode 100644 cli/.gitignore delete mode 100644 cli/README.md delete mode 100644 cli/package-lock.json delete mode 100644 cli/package.json delete mode 100644 cli/src/client.ts delete mode 100644 cli/src/commands/accounts.ts delete mode 100644 cli/src/commands/config.ts delete mode 100644 cli/src/commands/configure.ts delete mode 100644 cli/src/commands/customers.ts delete mode 100644 cli/src/commands/quotes.ts delete mode 100644 cli/src/commands/receiver.ts delete mode 100644 cli/src/commands/sandbox.ts delete mode 100644 cli/src/commands/transactions.ts delete mode 100644 cli/src/commands/transfers.ts delete mode 100644 cli/src/config.ts delete mode 100644 cli/src/index.ts delete mode 100644 cli/src/output.ts delete mode 100644 cli/src/prompt.ts delete mode 100644 cli/src/validation.ts delete mode 100644 cli/tsconfig.json create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 grid-kotlin-client-okhttp/build.gradle.kts create mode 100644 grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt create mode 100644 grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt create mode 100644 grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/OkHttpClient.kt create mode 100644 grid-kotlin-core/build.gradle.kts create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClient.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPager.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPagerAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseDeserializer.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseSerializer.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/ClientOptions.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/DefaultSleeper.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/ObjectMappers.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Page.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/PageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Params.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachable.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableExecutorService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableSleeper.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/PrepareRequest.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Properties.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/RequestOptions.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Sleeper.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Timeout.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Utils.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/EmptyHandler.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/ErrorHandler.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/JsonHandler.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/StringHandler.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/Headers.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpClient.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpMethod.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequest.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBodies.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBody.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponseFor.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingHttpClient.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingStreamResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/QueryParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/RetryingHttpClient.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/StreamResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/BadRequestException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridInvalidDataException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridIoException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridRetryableException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridServiceException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/InternalServerException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/NotFoundException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/PermissionDeniedException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/RateLimitException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnauthorizedException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnexpectedStatusCodeException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnprocessableEntityException.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigRetrieveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigUpdateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/CustomerInfoFieldName.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformConfig.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformCurrencyConfig.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/Customer.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreate.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerOneOf.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdate.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOf.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccount.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreate.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOf.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/CurrencyAmount.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCancelParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationClaimParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationRetrieveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/UmaInvitation.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseDestination.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfo.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseQuoteSource.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Currency.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/OutgoingRateDetails.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/PaymentInstructions.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Quote.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOf.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteExecuteParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteRetrieveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteSourceOneOf.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinition.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/LookupResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccount.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/ApiToken.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/Permission.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenDeleteParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenRetrieveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/BaseTransactionSource.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/IncomingTransaction.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionApproveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRejectParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRetrieveParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionSourceOneOf.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionStatus.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionType.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/BaseTransactionDestination.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/Transaction.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/TransferInCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferout/TransferOutCreateParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPage.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestParams.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponse.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsync.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaService.kt create mode 100644 grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceImpl.kt create mode 100644 grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/TestServerExtension.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/ClientOptionsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/ObjectMappersTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/PhantomReachableTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/UtilsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/ValuesTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HeadersTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HttpRequestTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/QueryParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/RetryingHttpClientTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigRetrieveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigUpdateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformConfigTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformCurrencyConfigTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerOneOfTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOfTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOfTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/CurrencyAmountTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCancelParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationClaimParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationRetrieveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/UmaInvitationTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseDestinationTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfoTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseQuoteSourceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/CurrencyTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/OutgoingRateDetailsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/PaymentInstructionsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOfTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteExecuteParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteRetrieveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteSourceOneOfTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinitionTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/LookupResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/ApiTokenTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenDeleteParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenRetrieveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/BaseTransactionSourceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/IncomingTransactionTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionApproveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRejectParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRetrieveParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionSourceOneOfTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/BaseTransactionDestinationTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransactionTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransferInCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferout/TransferOutCreateParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponseTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/ErrorHandlingTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/ServiceParamsTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ConfigServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/CustomerServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/InvitationServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlaidServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlatformServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/QuoteServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ReceiverServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/SandboxServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TokenServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransactionServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferInServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferOutServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/WebhookServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ConfigServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/CustomerServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ExchangeRateServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/InvitationServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlaidServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlatformServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/QuoteServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ReceiverServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/SandboxServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TokenServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransactionServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferInServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferOutServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/UmaProviderServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/WebhookServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/BulkServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceTest.kt create mode 100644 grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceTest.kt create mode 100644 grid-kotlin-example/build.gradle.kts create mode 100644 grid-kotlin-lib/.keep create mode 100644 grid-kotlin-proguard-test/build.gradle.kts create mode 100644 grid-kotlin-proguard-test/src/test/kotlin/com/grid/api/proguard/ProGuardCompatibilityTest.kt create mode 100644 grid-kotlin-proguard-test/test.pro create mode 100644 grid-kotlin/build.gradle.kts delete mode 100644 mintlify/AGENTS.md delete mode 100644 mintlify/CLAUDE.md delete mode 100644 mintlify/README.md delete mode 100644 mintlify/api-reference/authentication.mdx delete mode 100644 mintlify/api-reference/environments.mdx delete mode 100644 mintlify/api-reference/terminology.mdx delete mode 100644 mintlify/developer-resources/samples.mdx delete mode 100644 mintlify/docs.json delete mode 100644 mintlify/favicon.svg delete mode 100644 mintlify/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2 delete mode 100644 mintlify/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2 delete mode 100644 mintlify/fonts/suisse-intl/SuisseIntl-Book.woff2 delete mode 100644 mintlify/fonts/suisse-intl/SuisseIntl-Medium.woff2 delete mode 100644 mintlify/fonts/suisse-intl/SuisseIntl-Regular.woff2 delete mode 100644 mintlify/global-p2p/country-support.mdx delete mode 100644 mintlify/global-p2p/getting-started/implementation-overview.mdx delete mode 100644 mintlify/global-p2p/getting-started/platform-configuration.mdx delete mode 100644 mintlify/global-p2p/index.mdx delete mode 100644 mintlify/global-p2p/managing-accounts/external-accounts.mdx delete mode 100644 mintlify/global-p2p/managing-accounts/internal-accounts.mdx delete mode 100644 mintlify/global-p2p/managing-accounts/plaid.mdx delete mode 100644 mintlify/global-p2p/onboarding-customers/configuring-customers.mdx delete mode 100644 mintlify/global-p2p/onboarding-customers/invitations.mdx delete mode 100644 mintlify/global-p2p/platform-tools/postman-collection.mdx delete mode 100644 mintlify/global-p2p/platform-tools/sandbox-testing.mdx delete mode 100644 mintlify/global-p2p/platform-tools/uma-test-wallet.mdx delete mode 100644 mintlify/global-p2p/platform-tools/webhooks.mdx delete mode 100644 mintlify/global-p2p/quickstart.mdx delete mode 100644 mintlify/global-p2p/sending-receiving-payments/depositing-funds.mdx delete mode 100644 mintlify/global-p2p/sending-receiving-payments/error-handling.mdx delete mode 100644 mintlify/global-p2p/sending-receiving-payments/receiving-payments.mdx delete mode 100644 mintlify/global-p2p/sending-receiving-payments/reconciliation.mdx delete mode 100644 mintlify/global-p2p/sending-receiving-payments/sending-payments.mdx delete mode 100644 mintlify/global-p2p/terminology.mdx delete mode 100644 mintlify/images/IconArrowTopBottom.svg delete mode 100644 mintlify/images/IconChevronDoubleUp.svg delete mode 100644 mintlify/images/IconGift.svg delete mode 100644 mintlify/images/IconGlobus-green.svg delete mode 100644 mintlify/images/IconGlobus.svg delete mode 100644 mintlify/images/IconHammer-white.svg delete mode 100644 mintlify/images/IconHammer.svg delete mode 100644 mintlify/images/blue-lines-sm.svg delete mode 100644 mintlify/images/blue-lines.svg delete mode 100644 mintlify/images/entities.png delete mode 100644 mintlify/images/github-white.svg delete mode 100644 mintlify/images/github.svg delete mode 100644 mintlify/images/grid-white.svg delete mode 100644 mintlify/images/grid.svg delete mode 100644 mintlify/images/linkedin.svg delete mode 100644 mintlify/images/postman-white.svg delete mode 100644 mintlify/images/postman.svg delete mode 100644 mintlify/images/x-twitter.svg delete mode 100644 mintlify/index.mdx delete mode 100644 mintlify/logo/dark.svg delete mode 100644 mintlify/logo/light.svg delete mode 100644 mintlify/openapi.yaml delete mode 100644 mintlify/payouts-and-b2b/depositing-funds/depositing-funds.mdx delete mode 100644 mintlify/payouts-and-b2b/depositing-funds/external-accounts.mdx delete mode 100644 mintlify/payouts-and-b2b/depositing-funds/internal-accounts.mdx delete mode 100644 mintlify/payouts-and-b2b/depositing-funds/plaid.mdx delete mode 100644 mintlify/payouts-and-b2b/index.mdx delete mode 100644 mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx delete mode 100644 mintlify/payouts-and-b2b/onboarding/implementation-overview.mdx delete mode 100644 mintlify/payouts-and-b2b/onboarding/platform-configuration.mdx delete mode 100644 mintlify/payouts-and-b2b/payment-flow/error-handling.mdx delete mode 100644 mintlify/payouts-and-b2b/payment-flow/list-transactions.mdx delete mode 100644 mintlify/payouts-and-b2b/payment-flow/reconciliation.mdx delete mode 100644 mintlify/payouts-and-b2b/payment-flow/send-payment.mdx delete mode 100644 mintlify/payouts-and-b2b/platform-tools/postman-collection.mdx delete mode 100644 mintlify/payouts-and-b2b/platform-tools/sandbox-testing.mdx delete mode 100644 mintlify/payouts-and-b2b/platform-tools/webhooks.mdx delete mode 100644 mintlify/payouts-and-b2b/quickstart.mdx delete mode 100644 mintlify/payouts-and-b2b/terminology.mdx delete mode 100644 mintlify/platform-overview/core-concepts/account-model.mdx delete mode 100644 mintlify/platform-overview/core-concepts/currencies-and-rails.mdx delete mode 100644 mintlify/platform-overview/core-concepts/entities.mdx delete mode 100644 mintlify/platform-overview/core-concepts/quote-system.mdx delete mode 100644 mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx delete mode 100644 mintlify/platform-overview/introduction/faq.mdx delete mode 100644 mintlify/platform-overview/introduction/platform-capabilities.mdx delete mode 100644 mintlify/platform-overview/introduction/what-is-grid.mdx delete mode 100644 mintlify/ramps/accounts/depositing-funds.mdx delete mode 100644 mintlify/ramps/accounts/external-accounts.mdx delete mode 100644 mintlify/ramps/accounts/internal-accounts.mdx delete mode 100644 mintlify/ramps/accounts/plaid.mdx delete mode 100644 mintlify/ramps/conversion-flows/fiat-crypto-conversion.mdx delete mode 100644 mintlify/ramps/conversion-flows/self-custody-wallets.mdx delete mode 100644 mintlify/ramps/index.mdx delete mode 100644 mintlify/ramps/onboarding/configuring-customers.mdx delete mode 100644 mintlify/ramps/onboarding/implementation-overview.mdx delete mode 100644 mintlify/ramps/onboarding/platform-configuration.mdx delete mode 100644 mintlify/ramps/platform-tools/postman-collection.mdx delete mode 100644 mintlify/ramps/platform-tools/sandbox-testing.mdx delete mode 100644 mintlify/ramps/platform-tools/webhooks.mdx delete mode 100644 mintlify/ramps/quickstart.mdx delete mode 100644 mintlify/ramps/terminology.mdx delete mode 100644 mintlify/ramps/webhooks.mdx delete mode 100644 mintlify/rewards/developer-guides/configuring-customers.mdx delete mode 100644 mintlify/rewards/developer-guides/distributing-rewards.mdx delete mode 100644 mintlify/rewards/developer-guides/external-accounts.mdx delete mode 100644 mintlify/rewards/developer-guides/implementation-overview.mdx delete mode 100644 mintlify/rewards/developer-guides/internal-accounts.mdx delete mode 100644 mintlify/rewards/developer-guides/listing-transactions.mdx delete mode 100644 mintlify/rewards/developer-guides/plaid.mdx delete mode 100644 mintlify/rewards/developer-guides/platform-configuration.mdx delete mode 100644 mintlify/rewards/index.mdx delete mode 100644 mintlify/rewards/platform-tools/postman-collection.mdx delete mode 100644 mintlify/rewards/platform-tools/sandbox-testing.mdx delete mode 100644 mintlify/rewards/platform-tools/webhooks.mdx delete mode 100644 mintlify/rewards/quickstart.mdx delete mode 100644 mintlify/rewards/terminology.mdx delete mode 100644 mintlify/snippets/country-support.mdx delete mode 100644 mintlify/snippets/creating-customers/customer-types.mdx delete mode 100644 mintlify/snippets/creating-customers/customers.mdx delete mode 100644 mintlify/snippets/creating-customers/onboarding-model.mdx delete mode 100644 mintlify/snippets/depositing-funds.mdx delete mode 100644 mintlify/snippets/error-handling.mdx delete mode 100644 mintlify/snippets/external-accounts.mdx delete mode 100644 mintlify/snippets/internal-accounts.mdx delete mode 100644 mintlify/snippets/kyc/kyc-regulated.mdx delete mode 100644 mintlify/snippets/kyc/kyc-unregulated.mdx delete mode 100644 mintlify/snippets/kyc/kyc-webhooks.mdx delete mode 100644 mintlify/snippets/payment-flow.mdx delete mode 100644 mintlify/snippets/plaid-integration.mdx delete mode 100644 mintlify/snippets/platform-config-currency-api-webhooks.mdx delete mode 100644 mintlify/snippets/platform-configuration-non-uma.mdx delete mode 100644 mintlify/snippets/platform-configuration-uma.mdx delete mode 100644 mintlify/snippets/postman-collection.mdx delete mode 100644 mintlify/snippets/reconciliation/reconciliation.mdx delete mode 100644 mintlify/snippets/sending/cross-currency.mdx delete mode 100644 mintlify/snippets/sending/same-currency.mdx delete mode 100644 mintlify/snippets/sending/uma.mdx delete mode 100644 mintlify/snippets/terminology.mdx delete mode 100644 mintlify/snippets/uma-test-wallet.mdx delete mode 100644 mintlify/snippets/variables.mdx delete mode 100644 mintlify/snippets/webhooks.mdx delete mode 100644 mintlify/styles/fonts.css delete mode 100644 mintlify/styles/styles.css delete mode 100644 mintlify/styles/tokens.css delete mode 100644 openapi.yaml delete mode 100644 openapi/README.md delete mode 100644 openapi/components/schemas/common/Address.yaml delete mode 100644 openapi/components/schemas/common/BasePaymentAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/BaseWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/CadAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/ClabeAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/CounterpartyFieldDefinition.yaml delete mode 100644 openapi/components/schemas/common/Currency.yaml delete mode 100644 openapi/components/schemas/common/CurrencyAmount.yaml delete mode 100644 openapi/components/schemas/common/Error.yaml delete mode 100644 openapi/components/schemas/common/GbpAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/GridError.yaml delete mode 100644 openapi/components/schemas/common/IbanAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/NgnAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentAccountType.yaml delete mode 100644 openapi/components/schemas/common/PaymentBaseWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentClabeAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentIbanAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentInstructions.yaml delete mode 100644 openapi/components/schemas/common/PaymentLightningInvoiceInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentNgnAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentPixAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentPolygonWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentSolanaWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentSparkWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentTronWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentUpiAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PaymentUsAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PhpAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PixAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/PolygonWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/ReconciliationInstructions.yaml delete mode 100644 openapi/components/schemas/common/Refund.yaml delete mode 100644 openapi/components/schemas/common/SgdAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/SolanaWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/SparkWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/TronWalletInfo.yaml delete mode 100644 openapi/components/schemas/common/UpiAccountInfo.yaml delete mode 100644 openapi/components/schemas/common/UsAccountInfo.yaml delete mode 100644 openapi/components/schemas/config/PlatformConfig.yaml delete mode 100644 openapi/components/schemas/config/PlatformCurrencyConfig.yaml delete mode 100644 openapi/components/schemas/customers/BulkCustomerImportErrorEntry.yaml delete mode 100644 openapi/components/schemas/customers/BulkCustomerImportJob.yaml delete mode 100644 openapi/components/schemas/customers/BulkUploadWebhookRequest.yaml delete mode 100644 openapi/components/schemas/customers/BusinessCustomer.yaml delete mode 100644 openapi/components/schemas/customers/BusinessCustomerCreateRequest.yaml delete mode 100644 openapi/components/schemas/customers/BusinessCustomerFields.yaml delete mode 100644 openapi/components/schemas/customers/BusinessCustomerUpdateRequest.yaml delete mode 100644 openapi/components/schemas/customers/BusinessInfo.yaml delete mode 100644 openapi/components/schemas/customers/BusinessInfoUpdate.yaml delete mode 100644 openapi/components/schemas/customers/Customer.yaml delete mode 100644 openapi/components/schemas/customers/CustomerCreateRequest.yaml delete mode 100644 openapi/components/schemas/customers/CustomerCreateRequestOneOf.yaml delete mode 100644 openapi/components/schemas/customers/CustomerInfoFieldName.yaml delete mode 100644 openapi/components/schemas/customers/CustomerOneOf.yaml delete mode 100644 openapi/components/schemas/customers/CustomerType.yaml delete mode 100644 openapi/components/schemas/customers/CustomerUpdateRequest.yaml delete mode 100644 openapi/components/schemas/customers/CustomerUpdateRequestOneOf.yaml delete mode 100644 openapi/components/schemas/customers/IndividualCustomer.yaml delete mode 100644 openapi/components/schemas/customers/IndividualCustomerCreateRequest.yaml delete mode 100644 openapi/components/schemas/customers/IndividualCustomerFields.yaml delete mode 100644 openapi/components/schemas/customers/IndividualCustomerUpdateRequest.yaml delete mode 100644 openapi/components/schemas/customers/InternalAccount.yaml delete mode 100644 openapi/components/schemas/customers/KycStatus.yaml delete mode 100644 openapi/components/schemas/customers/UltimateBeneficialOwner.yaml delete mode 100644 openapi/components/schemas/errors/Error400.yaml delete mode 100644 openapi/components/schemas/errors/Error401.yaml delete mode 100644 openapi/components/schemas/errors/Error403.yaml delete mode 100644 openapi/components/schemas/errors/Error404.yaml delete mode 100644 openapi/components/schemas/errors/Error409.yaml delete mode 100644 openapi/components/schemas/errors/Error410.yaml delete mode 100644 openapi/components/schemas/errors/Error412.yaml delete mode 100644 openapi/components/schemas/errors/Error424.yaml delete mode 100644 openapi/components/schemas/errors/Error500.yaml delete mode 100644 openapi/components/schemas/errors/Error501.yaml delete mode 100644 openapi/components/schemas/external_accounts/BaseBeneficiary.yaml delete mode 100644 openapi/components/schemas/external_accounts/BaseExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/BeneficiaryOneOf.yaml delete mode 100644 openapi/components/schemas/external_accounts/BeneficiaryType.yaml delete mode 100644 openapi/components/schemas/external_accounts/BusinessBeneficiary.yaml delete mode 100644 openapi/components/schemas/external_accounts/CadAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/ClabeAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/ExternalAccount.yaml delete mode 100644 openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml delete mode 100644 openapi/components/schemas/external_accounts/ExternalAccountInfoOneOf.yaml delete mode 100644 openapi/components/schemas/external_accounts/ExternalAccountStatus.yaml delete mode 100644 openapi/components/schemas/external_accounts/ExternalAccountType.yaml delete mode 100644 openapi/components/schemas/external_accounts/GbpAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/IbanAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/IndividualBeneficiary.yaml delete mode 100644 openapi/components/schemas/external_accounts/LightningExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/NgnAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/PhpAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/PixAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/SgdAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/UpiAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/external_accounts/UsAccountExternalAccountInfo.yaml delete mode 100644 openapi/components/schemas/invitations/UmaInvitation.yaml delete mode 100644 openapi/components/schemas/plaid/PlaidCallbackRequest.yaml delete mode 100644 openapi/components/schemas/plaid/PlaidCallbackResponse.yaml delete mode 100644 openapi/components/schemas/plaid/PlaidLinkTokenRequest.yaml delete mode 100644 openapi/components/schemas/plaid/PlaidLinkTokenResponse.yaml delete mode 100644 openapi/components/schemas/quotes/AccountDestination.yaml delete mode 100644 openapi/components/schemas/quotes/AccountQuoteSource.yaml delete mode 100644 openapi/components/schemas/quotes/BaseDestination.yaml delete mode 100644 openapi/components/schemas/quotes/BaseQuoteSource.yaml delete mode 100644 openapi/components/schemas/quotes/DestinationType.yaml delete mode 100644 openapi/components/schemas/quotes/ExternalAccountDetailsDestination.yaml delete mode 100644 openapi/components/schemas/quotes/Quote.yaml delete mode 100644 openapi/components/schemas/quotes/QuoteDestinationOneOf.yaml delete mode 100644 openapi/components/schemas/quotes/QuoteLockSide.yaml delete mode 100644 openapi/components/schemas/quotes/QuoteRequest.yaml delete mode 100644 openapi/components/schemas/quotes/QuoteSourceOneOf.yaml delete mode 100644 openapi/components/schemas/quotes/QuoteSourceType.yaml delete mode 100644 openapi/components/schemas/quotes/RealtimeFundingQuoteSource.yaml delete mode 100644 openapi/components/schemas/quotes/UmaAddressDestination.yaml delete mode 100644 openapi/components/schemas/receiver/CurrencyPreference.yaml delete mode 100644 openapi/components/schemas/receiver/ReceiverLookupResponse.yaml delete mode 100644 openapi/components/schemas/tokens/ApiToken.yaml delete mode 100644 openapi/components/schemas/tokens/Permission.yaml delete mode 100644 openapi/components/schemas/transactions/AccountTransactionDestination.yaml delete mode 100644 openapi/components/schemas/transactions/AccountTransactionSource.yaml delete mode 100644 openapi/components/schemas/transactions/BaseTransactionDestination.yaml delete mode 100644 openapi/components/schemas/transactions/BaseTransactionSource.yaml delete mode 100644 openapi/components/schemas/transactions/IncomingRateDetails.yaml delete mode 100644 openapi/components/schemas/transactions/IncomingTransaction.yaml delete mode 100644 openapi/components/schemas/transactions/IncomingTransactionFailureReason.yaml delete mode 100644 openapi/components/schemas/transactions/OutgoingRateDetails.yaml delete mode 100644 openapi/components/schemas/transactions/OutgoingTransaction.yaml delete mode 100644 openapi/components/schemas/transactions/OutgoingTransactionFailureReason.yaml delete mode 100644 openapi/components/schemas/transactions/Transaction.yaml delete mode 100644 openapi/components/schemas/transactions/TransactionDestinationOneOf.yaml delete mode 100644 openapi/components/schemas/transactions/TransactionDestinationType.yaml delete mode 100644 openapi/components/schemas/transactions/TransactionSourceOneOf.yaml delete mode 100644 openapi/components/schemas/transactions/TransactionSourceType.yaml delete mode 100644 openapi/components/schemas/transactions/TransactionStatus.yaml delete mode 100644 openapi/components/schemas/transactions/TransactionType.yaml delete mode 100644 openapi/components/schemas/transactions/UmaAddressTransactionDestination.yaml delete mode 100644 openapi/components/schemas/transactions/UmaAddressTransactionSource.yaml delete mode 100644 openapi/components/schemas/uma_providers/UmaProvider.yaml delete mode 100644 openapi/components/schemas/webhooks/AccountStatusWebhook.yaml delete mode 100644 openapi/components/schemas/webhooks/BaseWebhook.yaml delete mode 100644 openapi/components/schemas/webhooks/IncomingPaymentWebhook.yaml delete mode 100644 openapi/components/schemas/webhooks/IncomingPaymentWebhookForbiddenResponse.yaml delete mode 100644 openapi/components/schemas/webhooks/IncomingPaymentWebhookResponse.yaml delete mode 100644 openapi/components/schemas/webhooks/IncomingPaymentWebhookUnprocessableResponse.yaml delete mode 100644 openapi/components/schemas/webhooks/InvitationClaimedWebhook.yaml delete mode 100644 openapi/components/schemas/webhooks/KycStatusWebhook.yaml delete mode 100644 openapi/components/schemas/webhooks/OutgoingPaymentWebhook.yaml delete mode 100644 openapi/components/schemas/webhooks/TestWebhookRequest.yaml delete mode 100644 openapi/components/schemas/webhooks/TestWebhookResponse.yaml delete mode 100644 openapi/components/schemas/webhooks/WebhookType.yaml delete mode 100644 openapi/openapi.yaml delete mode 100644 openapi/paths/account/account.yaml delete mode 100644 openapi/paths/customers/customers.yaml delete mode 100644 openapi/paths/customers/customers_bulk_csv.yaml delete mode 100644 openapi/paths/customers/customers_bulk_jobs_{jobId}.yaml delete mode 100644 openapi/paths/customers/customers_external_accounts.yaml delete mode 100644 openapi/paths/customers/customers_internal_accounts.yaml delete mode 100644 openapi/paths/customers/customers_kyc_link.yaml delete mode 100644 openapi/paths/customers/customers_{customerId}.yaml delete mode 100644 openapi/paths/internal-accounts/internal_accounts.yaml delete mode 100644 openapi/paths/invitations/invitations.yaml delete mode 100644 openapi/paths/invitations/invitations_{invitationCode}.yaml delete mode 100644 openapi/paths/invitations/invitations_{invitationCode}_cancel.yaml delete mode 100644 openapi/paths/invitations/invitations_{invitationCode}_claim.yaml delete mode 100644 openapi/paths/plaid/plaid_callback_{plaid_link_token}.yaml delete mode 100644 openapi/paths/plaid/plaid_link-tokens.yaml delete mode 100644 openapi/paths/platform/config.yaml delete mode 100644 openapi/paths/platform/platform_external_accounts.yaml delete mode 100644 openapi/paths/platform/platform_internal_accounts.yaml delete mode 100644 openapi/paths/quotes/quotes.yaml delete mode 100644 openapi/paths/quotes/quotes_{quoteId}.yaml delete mode 100644 openapi/paths/quotes/quotes_{quoteId}_execute.yaml delete mode 100644 openapi/paths/receiver/external-account/receiver_external-account_{accountId}.yaml delete mode 100644 openapi/paths/receiver/uma/receiver_uma_{receiverUmaAddress}.yaml delete mode 100644 openapi/paths/sandbox/sandbox_internal_accounts_{accountId}_fund.yaml delete mode 100644 openapi/paths/sandbox/sandbox_send.yaml delete mode 100644 openapi/paths/sandbox/sandbox_uma_receive.yaml delete mode 100644 openapi/paths/tokens/tokens.yaml delete mode 100644 openapi/paths/tokens/tokens_{tokenId}.yaml delete mode 100644 openapi/paths/transactions/transactions.yaml delete mode 100644 openapi/paths/transactions/transactions_{transactionId}.yaml delete mode 100644 openapi/paths/transactions/transactions_{transactionId}_approve.yaml delete mode 100644 openapi/paths/transactions/transactions_{transactionId}_reject.yaml delete mode 100644 openapi/paths/transfers/transfer_in.yaml delete mode 100644 openapi/paths/transfers/transfer_out.yaml delete mode 100644 openapi/paths/uma_providers/uma_providers.yaml delete mode 100644 openapi/paths/webhooks/webhooks_test.yaml delete mode 100644 openapi/webhooks/account-status.yaml delete mode 100644 openapi/webhooks/bulk-upload.yaml delete mode 100644 openapi/webhooks/incoming-payment.yaml delete mode 100644 openapi/webhooks/invitation-claimed.yaml delete mode 100644 openapi/webhooks/kyc-status.yaml delete mode 100644 openapi/webhooks/outgoing-payment.yaml delete mode 100644 openapi/webhooks/test-webhook.yaml delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100755 scripts/build create mode 100755 scripts/detect-breaking-changes create mode 100755 scripts/format create mode 100755 scripts/lint create mode 100755 scripts/mock create mode 100755 scripts/test create mode 100644 settings.gradle.kts diff --git a/.claude/skills/grid-api/SKILL.md b/.claude/skills/grid-api/SKILL.md deleted file mode 100644 index 28293a70..00000000 --- a/.claude/skills/grid-api/SKILL.md +++ /dev/null @@ -1,379 +0,0 @@ ---- -name: grid-api -description: > - This skill should be used when the user asks to "send a payment", "check balance", - "list transactions", "create a quote", "manage customers", "create external account", - "what currencies does Grid support", "how do I use the Grid API", "send money to [country]", - "pay [UMA address]", "send to CLABE", "send to PIX", "send to IBAN", "send to UPI", - "fund sandbox account", "test a payment", "on-ramp", "off-ramp", "convert crypto to fiat", - "convert fiat to crypto", "look up UMA", "real-time quote", "JIT funding", or any payment - operations using the Grid API CLI. -allowed-tools: - - Bash - - Read - - Grep - - Glob ---- - -# Grid API Skill - -You are a Grid API assistant that helps users manage global payments. You can: - -1. **Execute API Operations** - Use the CLI tool at `cli/dist/index.js` to interact with the Grid API -2. **Answer Documentation Questions** - Read the OpenAPI spec and Mintlify docs in this repo -3. **Guide Payment Workflows** - Help users send payments to bank accounts, UMA addresses, and crypto wallets - -## Supporting References - -For detailed information, read these reference files in the `references/` directory: - -- **`references/account-types.md`** - Country-specific external account requirements (CLABE, PIX, IBAN, UPI, etc.) with field requirements and CLI examples. Read this when creating external accounts for international payments. -- **`references/endpoints.md`** - Complete API endpoint reference with methods, paths, and response codes. Read this when answering questions about specific API capabilities. -- **`references/workflows.md`** - Step-by-step payment workflow guides for common scenarios (UMA payments, international transfers, on-ramp, off-ramp). Read this when guiding users through multi-step payment flows. - -## Key Concepts - -### Entities -- **Platform**: The top-level entity (the API user's business) -- **Customer**: End users of the platform who send/receive payments -- **Internal Account**: Grid-managed account for holding funds (can be platform-owned or customer-owned) -- **External Account**: Bank account or crypto wallet outside Grid (destination for payouts) -- **UMA Address**: Universal Money Address (e.g., `$user@domain.com`) for receiving payments - -### Account Types -- **Platform internal accounts**: For pooled funds, rewards distribution, treasury -- **Customer internal accounts**: Per-currency holding accounts created automatically for each customer -- **External accounts**: Traditional bank accounts (CLABE, IBAN, UPI, etc.) or crypto wallets - -### Account Status Lifecycle -`PENDING` → `ACTIVE` → `UNDER_REVIEW` → `INACTIVE` - -### Currency & Amounts -- All amounts are in the **smallest currency unit** (cents for USD, satoshis for BTC) -- Use the `currency.decimals` field to convert for display (USD=2, BTC=8, etc.) -- Example: `10000` with `decimals: 2` = $100.00 - -## Configuration - -**Prerequisites**: Build the CLI before first use: -```bash -cd cli && npm install && npm run build && cd .. -``` - -### Quick Setup (Recommended) - -Run the interactive configuration command: -```bash -node cli/dist/index.js configure -``` - -This will: -1. Prompt for your API Token ID and Client Secret -2. Validate the credentials against the API -3. Save them to `~/.grid-credentials` - -### Alternative: Environment Variables - -You can also configure via environment variables: -- `GRID_API_TOKEN_ID` - API token ID -- `GRID_API_CLIENT_SECRET` - API client secret -- `GRID_BASE_URL` - Base URL (defaults to `https://api.lightspark.com/grid/2025-10-13`) - -### Non-Interactive Setup - -```bash -node cli/dist/index.js configure --token-id --client-secret -``` - -## CLI Commands - -Run all CLI commands from the repo root using: `node cli/dist/index.js ` - -### Command Aliases - -For faster typing, these aliases are available: -- `customers` → `cust` -- `transactions` → `tx` -- `accounts` → `acct` - -Example: `node cli/dist/index.js cust list` is equivalent to `node cli/dist/index.js customers list` - -### Output Options - -By default, all commands output colorized JSON. You can change this: - -```bash -# Table format (human-readable) -node cli/dist/index.js customers list --format table - -# Disable colors (for piping/scripting) -node cli/dist/index.js customers list --no-color -``` - -### Platform Configuration -```bash -node cli/dist/index.js config get # Get platform config (currencies, limits) -node cli/dist/index.js config update --webhook-endpoint -``` - -### Customer Management -```bash -node cli/dist/index.js customers list [--limit N] -node cli/dist/index.js customers get -node cli/dist/index.js customers create --platform-id --type INDIVIDUAL --full-name "Name" -node cli/dist/index.js customers update --full-name "New Name" -node cli/dist/index.js customers delete # Prompts for confirmation -node cli/dist/index.js customers delete --yes # Skip confirmation -node cli/dist/index.js customers kyc-link --customer-id --redirect-url -``` - -### Account Management -```bash -# Internal accounts (Grid-managed, for holding funds) -node cli/dist/index.js accounts internal list [--customer-id ] # Customer internal accounts -node cli/dist/index.js accounts internal list --platform # Platform internal accounts - -# External accounts (bank accounts, crypto wallets - destinations for payouts) -node cli/dist/index.js accounts external list [--customer-id ] - -# Create external accounts by country/type: -# IMPORTANT: For INDIVIDUAL beneficiaries, ALWAYS include: -# --beneficiary-birth-date YYYY-MM-DD --beneficiary-nationality <2-letter-code> -# For BUSINESS beneficiaries, use --beneficiary-name with the legal business name - -# Mexico (CLABE) - Business example -node cli/dist/index.js accounts external create --customer-id --currency MXN --account-type CLABE --clabe <18-digit-number> --beneficiary-type BUSINESS --beneficiary-name "Company Name" - -# Mexico (CLABE) - Individual example -node cli/dist/index.js accounts external create --customer-id --currency MXN --account-type CLABE --clabe <18-digit-number> --beneficiary-type INDIVIDUAL --beneficiary-name "Full Name" --beneficiary-birth-date 1990-01-15 --beneficiary-nationality MX - -# India (UPI) -node cli/dist/index.js accounts external create --customer-id --currency INR --account-type UPI --upi-id "name@bank" --beneficiary-type INDIVIDUAL --beneficiary-name "Name" --beneficiary-birth-date YYYY-MM-DD --beneficiary-nationality IN - -# Nigeria (NGN) - REQUIRES: --bank-name (NOT --bank-code), --purpose -node cli/dist/index.js accounts external create --customer-id --currency NGN --account-type NGN_ACCOUNT --account-number <10-digit> --bank-name "GTBank" --purpose GOODS_OR_SERVICES --beneficiary-type INDIVIDUAL --beneficiary-name "Name" --beneficiary-birth-date YYYY-MM-DD --beneficiary-nationality NG -``` - -### Quotes (Cross-Currency Transfers) -```bash -# Account-funded to UMA: Use when funds are already in an internal account -node cli/dist/index.js quotes create --source-account --dest-uma
--amount 10000 --lock-side SENDING - -# Account-funded to external account: IMPORTANT - always include --dest-currency -node cli/dist/index.js quotes create --source-account --dest-account --dest-currency --amount 10000 --lock-side SENDING - -# Real-time/JIT funded: Use when user will provide funds at execution time via instant payment -# Returns paymentInstructions for funding. Only use with instant settlement methods. -# Destination can be internal account, external account, or UMA address -node cli/dist/index.js quotes create --source-customer --source-currency --dest-account --dest-currency --amount 10000 --lock-side RECEIVING - -node cli/dist/index.js quotes execute -node cli/dist/index.js quotes list [--status PENDING] -node cli/dist/index.js quotes get -``` - -**IMPORTANT**: When using `--dest-account`, you MUST also specify `--dest-currency`. This is required even though the external account already has a currency. - -### Same-Currency Transfers -```bash -node cli/dist/index.js transfers in --source --dest --amount 10000 -node cli/dist/index.js transfers out --source --dest --amount 10000 -``` - -### Transactions -```bash -node cli/dist/index.js transactions list [--status PENDING] -node cli/dist/index.js transactions get -node cli/dist/index.js transactions approve -node cli/dist/index.js transactions reject [--reason "reason"] -``` - -### Receiver Lookup -```bash -node cli/dist/index.js receiver lookup-uma # Get UMA payment capabilities -node cli/dist/index.js receiver lookup-account # Get account payment capabilities -``` - -### Sandbox Testing -```bash -node cli/dist/index.js sandbox fund --amount 100000 # Fund account in sandbox -node cli/dist/index.js sandbox send --quote-id --currency # Simulate sending funds to a real-time quote -node cli/dist/index.js sandbox receive --uma-address --amount 1000 --currency USD -``` - -## Payment Flow Patterns - -Grid supports three main payment patterns: - -### 1. Prefunded (Account-Funded) -Funds are already in an internal account. Quote executes immediately from existing balance. -``` -Internal Account (USD) → Quote → External Account/UMA (EUR) -``` -Use `--source-account` with an internal account ID. - -### 2. Just-in-Time (Real-Time Funded) -Funds will be provided at execution time. Quote returns `paymentInstructions` with multiple funding options. Grid auto-executes when deposit is received. -``` -Customer sends crypto/fiat → Grid detects deposit → Auto-executes at locked rate -``` -Use `--source-customer` + `--source-currency`. Only works with instant settlement methods. - -### 3. Same-Currency Transfers -Direct transfers between accounts without currency conversion. No quote needed. -``` -External Account (USD) → Internal Account (USD) [transfer-in] -Internal Account (USD) → External Account (USD) [transfer-out] -``` - -## Interactive Payment Workflows - -When a user wants to send a payment, guide them through these steps: - -### Sending to an UMA Address - -1. Look up the receiver: `node cli/dist/index.js receiver lookup-uma $user@domain.com` -2. Show supported currencies and any required payer data -3. Create a quote: `node cli/dist/index.js quotes create --source-account --dest-uma
--amount --lock-side SENDING` -4. Show exchange rate and fees from the quote response -5. Ask for confirmation before executing -6. Execute: `node cli/dist/index.js quotes execute ` - -### Sending to a Bank Account (International) - -1. Determine the destination country and use the appropriate account type: - - **Mexico**: CLABE (18 digits) - - **Brazil**: PIX (CPF 11 digits, CNPJ 14 digits, Email, Phone, or EVP 32 chars) - - **India**: UPI (VPA format: `user@bankhandle`) - - **Nigeria**: NGN_ACCOUNT (10-digit account + bank name + purpose) - - **Europe**: IBAN + SWIFT BIC - - **US**: Routing number (9 digits) + Account number - - **Crypto**: Spark wallet (`spark1...`), Solana/Base/Polygon/Tron addresses - -2. Collect required information: - - Account details specific to the type - - Beneficiary info (requirements vary by destination - check API response for errors) - -3. Create external account using the CLI (see Account Management section for examples) - -4. Create quote showing exchange rate and fees - -5. Get confirmation and execute (or for JIT, show payment instructions) - -### Real-Time / Just-in-Time Funded Transfers - -Use this flow when the user asks for a "realtime quote" or "just in time" funded transfer. The quote response includes `paymentInstructions` for how to fund the transfer. - -**Key concept:** Real-time funding is about instant settlement, not currency type. Use this when funds will be provided at execution time via any instant payment method: -- **Crypto:** BTC (Lightning, Spark), USDC (Solana, Base, Polygon), USDT (Tron) -- **Fiat:** RTP, SEPA Instant, and other instant payment rails - -**Important:** Only use real-time funding with instant settlement methods. Do not use with slow methods like ACH since quotes expire quickly. - -1. Create a quote with `--source-customer` and `--source-currency`. Destination can be an internal account, external account, or UMA address: - ```bash - node cli/dist/index.js quotes create \ - --source-customer \ - --source-currency BTC \ - --dest-account \ - --dest-currency USD \ - --amount 100000 \ - --lock-side RECEIVING - ``` - -2. The response includes `paymentInstructions` with **multiple funding options simultaneously**: - - For BTC: Lightning invoice AND Spark wallet address - - For USDC: Solana wallet AND Base wallet AND Polygon wallet - - For fiat: RTP details or SEPA Instant details - - The user can choose any one of these options to fund the quote. - -3. Show the user ALL payment options and the exchange rate. Let them choose their preferred method. - -4. **Auto-execution**: Once the user sends funds to ANY of the provided addresses, Grid automatically: - - Detects the deposit (monitors blockchain/payment rails) - - Executes the transfer at the locked exchange rate - - Credits the destination account - - Sends webhooks: `ACCOUNT_STATUS` on deposit, `OUTGOING_PAYMENT` on completion - -5. **No manual execution needed** - Do NOT call `quotes execute` for JIT quotes. The transfer executes automatically when funds are received. - -6. **Quote expiration**: Quotes expire in 1-5 minutes. If expired, create a new quote. Do not use slow settlement methods (ACH) with JIT funding. - -## Documentation Resources - -For questions about the Grid API: -- **OpenAPI Spec**: `openapi/` directory for endpoint details and request/response schemas -- **Mintlify Docs**: `mintlify/` directory for guides, concepts, and workflow explanations - - `mintlify/snippets/` - Reusable content on accounts, transfers, KYC, etc. - - `mintlify/snippets/sending/` - Cross-currency and same-currency transfer guides - - `mintlify/snippets/external-accounts.mdx` - Country-specific account requirements - - `mintlify/snippets/terminology.mdx` - Entity definitions and relationships -- **External Account Schemas**: `openapi/components/schemas/external_accounts/` for field requirements per account type - -## Best Practices - -1. **Check platform config first**: Run `config get` to see supported currencies and required fields -2. **Use smallest currency units**: All amounts are in cents/satoshis - use `decimals` field for display -3. **Handle quote expiration**: Quotes expire in 1-5 minutes; be prepared to create new quotes -4. **Choose the right flow**: Use prefunded for immediate execution, JIT for crypto/instant rails -5. **Input validation**: The CLI validates dates (YYYY-MM-DD), amounts, and currencies before making API calls - you'll get clear error messages for invalid input -6. **Use table format for exploration**: `--format table` makes it easier to scan results interactively - -## Common Mistakes to Avoid - -### External Account Creation -1. **Missing individual beneficiary fields**: For `--beneficiary-type INDIVIDUAL`, you MUST include: - - `--beneficiary-birth-date YYYY-MM-DD` - - `--beneficiary-nationality <2-letter-code>` - -2. **Wrong option names**: - - Use `--bank-name` (NOT `--bank-code`) for Nigerian accounts - - Use `--purpose` for Nigerian accounts (required) - -3. **Missing country-specific fields**: - - Nigeria (NGN_ACCOUNT): Requires `--purpose` (e.g., `GOODS_OR_SERVICES`) - - Brazil (PIX): Requires `--pix-key` and `--pix-key-type` - - Europe (IBAN): Requires `--swift-bic` - -### Quote Creation -1. **Missing destination currency**: When using `--dest-account`, you MUST also include `--dest-currency `. This is required even though the external account already has a currency associated with it. - -## Error Handling - -All CLI commands output JSON with this structure: -```json -{ - "success": true, - "data": { ... } -} -``` - -Or on error: -```json -{ - "success": false, - "error": { - "code": "ERROR_CODE", - "message": "Human readable message" - } -} -``` - -### Common Error Codes - -**Quote/Transfer Errors:** -- `QUOTE_EXPIRED` - Quote timed out; create a new quote -- `INSUFFICIENT_BALANCE` - Check internal account balance before transfer -- `INVALID_BANK_ACCOUNT` - Validate field formats per country requirements -- `QUOTE_EXECUTION_FAILED` - Transient error; retry with exponential backoff - -**Incoming Payment Errors:** -- `PAYMENT_APPROVAL_TIMED_OUT` - Webhook approval not received within 5 seconds -- `PAYMENT_APPROVAL_WEBHOOK_ERROR` - Webhook returned error - -**Validation Errors:** -- `INVALID_INPUT` - Check required fields; the `reason` field has details -- `MISSING_MANDATORY_USER_INFO` - Customer or sender info missing required fields - -Always check the `success` field and report errors clearly to the user. diff --git a/.claude/skills/grid-api/references/account-types.md b/.claude/skills/grid-api/references/account-types.md deleted file mode 100644 index a6dc2cd7..00000000 --- a/.claude/skills/grid-api/references/account-types.md +++ /dev/null @@ -1,210 +0,0 @@ -# External Account Types by Country/Region - -This reference maps countries/regions to their required account types and fields for sending payments via the Grid API. - -**Note:** The CLI validates date formats (YYYY-MM-DD) and currency codes before making API calls. Invalid input will return a clear error message without hitting the API. - -## Account Type Summary - -| Country/Region | Account Type | Currency | Primary Identifier | -|----------------|--------------|----------|-------------------| -| Mexico | CLABE | MXN | 18-digit CLABE number | -| Brazil | PIX | BRL | PIX key (CPF/CNPJ/email/phone/random) | -| Europe (SEPA) | IBAN | EUR | IBAN number | -| United States | US_ACCOUNT | USD | Account + Routing number | -| India | UPI | INR | UPI ID (user@bank) | -| Nigeria | NGN_ACCOUNT | NGN | Account number + Bank code | - -## Crypto/Wallet Types - -| Type | Currency | Primary Identifier | -|------|----------|-------------------| -| SPARK_WALLET | BTC | Spark wallet address | -| LIGHTNING | BTC | Lightning invoice or address | -| SOLANA_WALLET | USDC | Solana wallet address | -| TRON_WALLET | USDT | Tron wallet address | -| POLYGON_WALLET | USDC | Polygon wallet address | -| BASE_WALLET | USDC | Base wallet address | - -## Detailed Field Requirements - -### Mexico (CLABE) - -**Individual beneficiary:** -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency MXN \ - --account-type CLABE \ - --clabe <18-digit-clabe> \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Full Name" \ - --beneficiary-birth-date "1990-01-15" \ - --beneficiary-nationality MX -``` - -**Business beneficiary:** -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency MXN \ - --account-type CLABE \ - --clabe <18-digit-clabe> \ - --beneficiary-type BUSINESS \ - --beneficiary-name "Company Legal Name" -``` - -Required fields: -- `clabeNumber`: 18-digit CLABE number (validates with check digit) -- For individuals: `fullName`, `birthDate`, `nationality` (ALL THREE REQUIRED) -- For businesses: `legalName` - -### Brazil (PIX) - -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency BRL \ - --account-type PIX \ - --pix-key "cpf:12345678901" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Full Name" -``` - -PIX key formats: -- CPF: 11 digits (e.g., `12345678901`) -- CNPJ: 14 digits (e.g., `12345678901234`) -- Email: valid email address -- Phone: +55 followed by number -- Random: 32-character alphanumeric EVP key - -### Europe (IBAN) - -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency EUR \ - --account-type IBAN \ - --iban "DE89370400440532013000" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Full Name" \ - --beneficiary-address-line1 "123 Street" \ - --beneficiary-address-city "Berlin" \ - --beneficiary-address-country DE -``` - -IBAN format: Country code (2) + Check digits (2) + BBAN (up to 30) - -### United States (US_ACCOUNT) - -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency USD \ - --account-type US_ACCOUNT \ - --routing-number "123456789" \ - --account-number "12345678901" \ - --account-category CHECKING \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Full Name" \ - --beneficiary-address-line1 "123 Main St" \ - --beneficiary-address-city "San Francisco" \ - --beneficiary-address-state CA \ - --beneficiary-address-postal "94105" \ - --beneficiary-address-country US -``` - -Required fields: -- `routingNumber`: 9-digit ABA routing number -- `accountNumber`: Bank account number -- `accountCategory`: CHECKING or SAVINGS - -### India (UPI) - -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency INR \ - --account-type UPI \ - --upi-id "user@okbank" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Full Name" -``` - -UPI ID format: `username@bankhandle` (e.g., `john@okaxis`, `jane@ybl`) - -### Nigeria (NGN_ACCOUNT) - -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency NGN \ - --account-type NGN_ACCOUNT \ - --account-number "1234567890" \ - --bank-name "GTBank" \ - --purpose GOODS_OR_SERVICES \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Full Name" \ - --beneficiary-birth-date "1990-05-15" \ - --beneficiary-nationality NG -``` - -Required fields: -- `accountNumber`: 10-digit account number -- `bankName`: Bank name (e.g., "GTBank", "Zenith Bank", "Access Bank") -- `purposeOfPayment`: Purpose code (e.g., `GOODS_OR_SERVICES`) -- For individuals: `fullName`, `birthDate`, `nationality` -- For businesses: `legalName` - -**IMPORTANT**: Use `--bank-name` (NOT `--bank-code`). Use the bank's display name. - -Common Nigerian banks: -- GTBank -- Zenith Bank -- United Bank for Africa -- Access Bank -- First Bank of Nigeria - -### Crypto Wallets - -#### Spark Wallet (Bitcoin) -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency BTC \ - --account-type SPARK_WALLET \ - --address "spark1..." -``` - -#### Solana Wallet (USDC) -```bash -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency USDC \ - --account-type SOLANA_WALLET \ - --address "..." -``` - -## Beneficiary Information - -**CRITICAL**: All external accounts require beneficiary information. The required fields depend on the beneficiary type. - -### For Individuals (--beneficiary-type INDIVIDUAL) -**All three fields are REQUIRED:** -- `--beneficiary-name`: Full legal name -- `--beneficiary-birth-date`: Date of birth (YYYY-MM-DD format) -- `--beneficiary-nationality`: 2-letter ISO country code (e.g., NG, MX, IN, US) -- `--beneficiary-address-*`: Optional but recommended - -### For Businesses (--beneficiary-type BUSINESS) -- `--beneficiary-name`: Registered business/legal name (REQUIRED) -- `--beneficiary-address-*`: Business address (optional) - -## Sandbox Testing - -In sandbox mode, use these account number patterns to test scenarios: -- Numbers ending in **002**: Insufficient funds -- Numbers ending in **003**: Account closed/invalid -- Numbers ending in **004**: Transfer rejected -- Numbers ending in **005**: Timeout/delayed failure -- Any other number: Success diff --git a/.claude/skills/grid-api/references/endpoints.md b/.claude/skills/grid-api/references/endpoints.md deleted file mode 100644 index eee1abe7..00000000 --- a/.claude/skills/grid-api/references/endpoints.md +++ /dev/null @@ -1,187 +0,0 @@ -# Grid API Endpoints Reference - -Quick reference for all Grid API endpoints. For full details, see `openapi/` directory. - -## Base URL - -Production: `https://api.lightspark.com/grid/2025-10-13` - -## Authentication - -HTTP Basic Auth: `Authorization: Basic base64(tokenId:clientSecret)` - -## Platform Configuration - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/config` | Get platform configuration | -| PATCH | `/config` | Update platform configuration | - -## Customers - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/customers` | Create a new customer | -| GET | `/customers` | List customers (paginated) | -| GET | `/customers/{customerId}` | Get customer details | -| PATCH | `/customers/{customerId}` | Update customer | -| DELETE | `/customers/{customerId}` | Delete customer | -| POST | `/customers/kyc-link` | Generate KYC link | -| POST | `/customers/bulk/csv` | Bulk upload customers | -| GET | `/customers/bulk/jobs/{jobId}` | Get bulk job status | - -## Internal Accounts - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/customers/internal-accounts` | List customer internal accounts | -| GET | `/platform/internal-accounts` | List platform internal accounts | - -Internal accounts are auto-created when customers are created based on platform config. - -## External Accounts - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/customers/external-accounts` | Create customer external account | -| GET | `/customers/external-accounts` | List customer external accounts | -| GET | `/platform/external-accounts` | List platform external accounts | - -## Same-Currency Transfers - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/transfer-in` | Transfer from external to internal (same currency) | -| POST | `/transfer-out` | Transfer from internal to external (same currency) | - -## Cross-Currency Transfers (Quotes) - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/quotes` | Create a transfer quote | -| GET | `/quotes` | List quotes (paginated) | -| GET | `/quotes/{quoteId}` | Get quote details | -| POST | `/quotes/{quoteId}/execute` | Execute a pending quote | - -### Quote Source Types -- `source.accountId`: Internal account ID -- `source.customerId` + `source.currency`: Customer-funded with payment instructions - -### Quote Destination Types -- `destination.accountId`: External account ID -- `destination.umaAddress` + `destination.currency`: UMA address -- `destination.externalAccountDetails`: Inline account creation - -## Receiver Lookup - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/receiver/uma/{receiverUmaAddress}` | Look up UMA address capabilities | -| GET | `/receiver/external-account/{accountId}` | Look up external account capabilities | - -Returns: supported currencies, min/max amounts, required payer data fields. - -## Transactions - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/transactions` | List transactions (paginated) | -| GET | `/transactions/{transactionId}` | Get transaction details | -| POST | `/transactions/{transactionId}/approve` | Approve incoming payment | -| POST | `/transactions/{transactionId}/reject` | Reject incoming payment | - -### Transaction Types -- `INCOMING`: Payment received -- `OUTGOING`: Payment sent - -### Transaction Statuses -- `PENDING`: Awaiting processing -- `PENDING_APPROVAL`: Needs approval (incoming) -- `PROCESSING`: In progress -- `COMPLETED`: Successfully completed -- `FAILED`: Failed -- `CANCELLED`: Cancelled - -## Webhooks - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/webhooks/test` | Send test webhook | - -### Webhook Events -- `incoming-payment`: Payment received -- `outgoing-payment`: Payment status update -- `kyc-status`: KYC status change -- `account-status`: Account status change -- `bulk-upload`: Bulk job completion -- `invitation-claimed`: Invitation claimed - -## Invitations - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/invitations` | Create invitation | -| GET | `/invitations` | List invitations | -| GET | `/invitations/{invitationCode}` | Get invitation | -| POST | `/invitations/{invitationCode}/claim` | Claim invitation | -| POST | `/invitations/{invitationCode}/cancel` | Cancel invitation | - -## Sandbox Testing - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/sandbox/send` | Simulate sending payment | -| POST | `/sandbox/uma/receive` | Simulate receiving UMA payment | -| POST | `/sandbox/internal-accounts/{accountId}/fund` | Fund account in sandbox | - -### Sandbox Account Patterns -Use these account number endings for testing: -- `002`: Insufficient funds -- `003`: Account closed/invalid -- `004`: Transfer rejected -- `005`: Timeout (pending ~30s, then fails) -- Other: Success - -## API Tokens - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/tokens` | Create API token | -| GET | `/tokens` | List API tokens | -| GET | `/tokens/{tokenId}` | Get token details | -| DELETE | `/tokens/{tokenId}` | Delete token | - -## UMA Providers - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/uma-providers` | List UMA provider domains | - -## Plaid Integration - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/plaid/link-tokens` | Create Plaid Link token | -| POST | `/plaid/callback/{plaid_link_token}` | Plaid Link callback | - -## Pagination - -List endpoints support cursor-based pagination: -- `limit`: Max results (default 20, max 100) -- `cursor`: Pagination cursor from previous response -- Response includes `hasMore`, `nextCursor`, `totalCount` - -## Common Response Codes - -| Code | Description | -|------|-------------| -| 200 | Success | -| 201 | Created | -| 400 | Bad request (invalid parameters) | -| 401 | Unauthorized (invalid credentials) | -| 404 | Not found | -| 409 | Conflict (duplicate) | -| 412 | Precondition failed (UMA version) | -| 424 | Counterparty issue | -| 500 | Internal server error | -| 501 | Not implemented | diff --git a/.claude/skills/grid-api/references/workflows.md b/.claude/skills/grid-api/references/workflows.md deleted file mode 100644 index 947d8e6b..00000000 --- a/.claude/skills/grid-api/references/workflows.md +++ /dev/null @@ -1,274 +0,0 @@ -# Common Payment Workflows - -This reference describes common payment workflows using the Grid API CLI. - -**Tip:** Use command aliases for faster typing: `cust` (customers), `tx` (transactions), `acct` (accounts). - -**Tip:** Add `--format table` to any list command for human-readable output. - -## Workflow 1: Send Payment to UMA Address - -Use this when the user wants to send money to an UMA address like `$alice@example.com`. - -### Steps - -1. **Look up the receiver** -```bash -node cli/dist/index.js receiver lookup-uma "\$alice@example.com" -``` - -This returns: -- Supported currencies -- Min/max amounts per currency -- Required payer data fields - -2. **Check sender's balance** -```bash -node cli/dist/index.js acct internal list --customer-id --format table -``` - -Identify the internal account with sufficient balance. - -3. **Create a quote** -```bash -node cli/dist/index.js quotes create \ - --source-account InternalAccount:xxx \ - --dest-uma "\$alice@example.com" \ - --dest-currency USD \ - --amount 10000 \ - --lock-side SENDING \ - --description "Payment for services" -``` - -The response includes: -- `sendingAmount` and `sendingCurrency` -- `receivingAmount` and `receivingCurrency` -- `exchangeRate` -- `fees` array -- `expiresAt` (quote validity) - -4. **Show the user the exchange details and get confirmation** - -Example: "You're sending $100.00 USD. The recipient will receive €92.15 EUR (rate: 0.9215). Fee: $1.50. Confirm?" - -5. **Execute the quote** -```bash -node cli/dist/index.js quotes execute Quote:xxx -``` - -6. **Monitor the transaction** -```bash -node cli/dist/index.js tx list --status PROCESSING --format table -``` - -## Workflow 2: Send Payment to International Bank Account - -Use this when the user wants to send money to a bank account in another country. - -### Steps - -1. **Determine destination country and account type** - -Ask the user: "Which country is the bank account in?" - -Refer to `account-types.md` for: -- Required account type (CLABE, PIX, IBAN, etc.) -- Required fields - -2. **Collect account details interactively** - -For Mexico (CLABE): -- Ask for 18-digit CLABE number -- Ask for beneficiary name -- Ask for beneficiary birth date -- Ask for beneficiary nationality - -3. **Create the external account** -```bash -node cli/dist/index.js accounts external create \ - --customer-id Customer:xxx \ - --currency MXN \ - --account-type CLABE \ - --clabe 012345678901234567 \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Juan Garcia" \ - --beneficiary-birth-date "1985-03-15" \ - --beneficiary-nationality MX -``` - -4. **Look up the account to get payment capabilities** -```bash -node cli/dist/index.js receiver lookup-account ExternalAccount:xxx -``` - -5. **Create a quote** -```bash -node cli/dist/index.js quotes create \ - --source-account InternalAccount:xxx \ - --dest-account ExternalAccount:xxx \ - --dest-currency MXN \ - --amount 100000 \ - --lock-side RECEIVING -``` - -Note: `--lock-side RECEIVING` means the recipient gets exactly this amount. - -6. **Show exchange details and confirm** - -7. **Execute the quote** -```bash -node cli/dist/index.js quotes execute Quote:xxx -``` - -## Workflow 3: On-Ramp (Fiat to Crypto) - -Use this when a customer wants to convert fiat to cryptocurrency. - -### Steps - -1. **Ensure customer has completed KYC** -```bash -node cli/dist/index.js customers get Customer:xxx -``` - -Check `kycStatus` is `APPROVED`. - -2. **Customer deposits fiat to their internal account** - -Show customer the `paymentInstructions` from their internal account: -```bash -node cli/dist/index.js accounts internal list --customer-id Customer:xxx -``` - -3. **Create external account for crypto destination** -```bash -node cli/dist/index.js accounts external create \ - --customer-id Customer:xxx \ - --currency BTC \ - --account-type SPARK_WALLET \ - --address "spark1..." -``` - -4. **Create and execute quote for conversion** -```bash -node cli/dist/index.js quotes create \ - --source-account InternalAccount:xxx \ - --dest-account ExternalAccount:xxx \ - --dest-currency BTC \ - --amount 10000000 \ - --lock-side SENDING \ - --immediate -``` - -The `--immediate` flag executes the quote instantly. - -## Workflow 4: Off-Ramp (Crypto to Fiat) - -Use this when a customer wants to convert cryptocurrency to fiat. - -### Steps - -1. **Create fiat external account for payout** -```bash -node cli/dist/index.js accounts external create \ - --customer-id Customer:xxx \ - --currency USD \ - --account-type US_ACCOUNT \ - --routing-number "123456789" \ - --account-number "12345678901" \ - --account-category CHECKING \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "John Doe" -``` - -2. **Customer sends crypto to their internal account** - -Provide the deposit address from their BTC internal account. - -3. **Create and execute quote for conversion** -```bash -node cli/dist/index.js quotes create \ - --source-account InternalAccount:xxx \ - --dest-account ExternalAccount:xxx \ - --dest-currency USD \ - --amount 50000 \ - --lock-side RECEIVING -``` - -## Workflow 5: Handle Incoming Payment - -Use this when your platform receives an incoming payment that needs approval. - -### Steps - -1. **List pending incoming transactions** -```bash -node cli/dist/index.js transactions list --type INCOMING --status PENDING_APPROVAL -``` - -2. **Review transaction details** -```bash -node cli/dist/index.js transactions get Transaction:xxx -``` - -3. **Approve or reject** -```bash -# Approve -node cli/dist/index.js transactions approve Transaction:xxx - -# Or reject -node cli/dist/index.js transactions reject Transaction:xxx --reason "Suspicious activity" -``` - -## Workflow 6: Bulk Customer Onboarding - -The API supports bulk customer creation via CSV upload. - -### Steps - -1. **Prepare CSV file** with columns: - - platformCustomerId - - customerType - - fullName (for individuals) - - birthDate - - address fields - -2. **Upload CSV** (this endpoint isn't in the CLI, use curl): -```bash -curl -X POST https://api.lightspark.com/grid/2025-10-13/customers/bulk/csv \ - -u "$GRID_API_TOKEN_ID:$GRID_API_CLIENT_SECRET" \ - -F "file=@customers.csv" -``` - -3. **Check job status** -```bash -node cli/dist/index.js customers bulk-status -``` - -## Error Recovery - -### Quote Expired - -If a quote expires before execution: -1. Create a new quote with the same parameters -2. Note: Exchange rates may have changed - -### Transaction Failed - -Check transaction status for failure reason: -```bash -node cli/dist/index.js transactions get Transaction:xxx -``` - -Common failure reasons: -- Insufficient funds -- Invalid account details -- Compliance rejection -- Bank rejection - -### Retry with Different Account - -If a bank account is rejected, try: -1. Verify account details are correct -2. Create a new external account with corrected details -3. Create new quote with the new account diff --git a/.cursor/commands/transfer-in.md b/.cursor/commands/transfer-in.md deleted file mode 100644 index e69de29b..00000000 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..bd8e2619 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libxkbcommon0 \ + ca-certificates \ + ca-certificates-java \ + make \ + curl \ + git \ + openjdk-17-jdk-headless \ + unzip \ + libc++1 \ + vim \ + && apt-get clean autoclean + +# Ensure UTF-8 encoding +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 + +WORKDIR /workspace + +COPY . /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..d55fc4d6 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + "build": { + "dockerfile": "Dockerfile" + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.fossa.yml b/.fossa.yml deleted file mode 100644 index ea951ebc..00000000 --- a/.fossa.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 3 - -project: - id: lightspark/grid-api - name: grid-api - url: https://github.com/lightsparkdev/grid-api \ No newline at end of file diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 4de36af3..00000000 --- a/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -# Generated files - GitHub will ignore these in PR reviews -openapi.yaml linguist-generated=true -mintlify/openapi.yaml linguist-generated=true - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..58c09b3b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,85 @@ +name: CI +on: + push: + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 15 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/grid-kotlin' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + + steps: + - uses: actions/checkout@v6 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Run lints + run: ./scripts/lint + + build: + timeout-minutes: 15 + name: build + runs-on: ${{ github.repository == 'stainless-sdks/grid-kotlin' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + + steps: + - uses: actions/checkout@v6 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Build SDK + run: ./scripts/build + + test: + timeout-minutes: 15 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/grid-kotlin' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v6 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run tests + run: ./scripts/test diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml new file mode 100644 index 00000000..fae14ad8 --- /dev/null +++ b/.github/workflows/detect-breaking-changes.yml @@ -0,0 +1,39 @@ +name: CI +on: + pull_request: + branches: + - main + - next + +jobs: + detect_breaking_changes: + runs-on: 'ubuntu-latest' + name: detect-breaking-changes + if: github.repository == 'stainless-sdks/grid-kotlin' + steps: + - name: Calculate fetch-depth + run: | + echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV + + - uses: actions/checkout@v6 + with: + # Ensure we can check out the pull request base in the script below. + fetch-depth: ${{ env.FETCH_DEPTH }} + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Detect breaking changes + run: | + # Try to check out previous versions of the breaking change detection script. This ensures that + # we still detect breaking changes when entire files and their tests are removed. + git checkout "${{ github.event.pull_request.base.sha }}" -- ./scripts/detect-breaking-changes 2>/dev/null || true + ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }} \ No newline at end of file diff --git a/.github/workflows/docs-sync.yml b/.github/workflows/docs-sync.yml deleted file mode 100644 index 37af7ac0..00000000 --- a/.github/workflows/docs-sync.yml +++ /dev/null @@ -1,165 +0,0 @@ -name: Nightly Documentation Sync - -on: - schedule: - # Run at 8AM UTC daily (12AM PST) - - cron: '0 8 * * *' - workflow_dispatch: - inputs: - hours_lookback: - description: 'Hours to look back for changes (default: 24)' - required: false - default: '24' - -permissions: - contents: write - pull-requests: write - issues: write - -jobs: - detect-changes: - runs-on: ubuntu-latest - outputs: - has_changes: ${{ steps.check.outputs.has_changes }} - changed_files: ${{ steps.check.outputs.changed_files }} - change_summary: ${{ steps.check.outputs.change_summary }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check for OpenAPI changes - id: check - run: | - HOURS="${{ github.event.inputs.hours_lookback || '24' }}" - SINCE=$(date -u -d "${HOURS} hours ago" '+%Y-%m-%dT%H:%M:%S') - - # Find commits touching openapi/ in the time window - CHANGED_FILES=$(git log --since="$SINCE" --name-only --pretty=format: -- 'openapi/' | sort -u | grep -v '^$' || true) - - if [ -z "$CHANGED_FILES" ]; then - echo "No OpenAPI changes in the last ${HOURS} hours" - echo "has_changes=false" >> $GITHUB_OUTPUT - else - echo "Found OpenAPI changes:" - echo "$CHANGED_FILES" - echo "has_changes=true" >> $GITHUB_OUTPUT - - # Store changed files (escape newlines for GitHub output) - echo "changed_files<> $GITHUB_OUTPUT - echo "$CHANGED_FILES" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - # Get a summary of what changed - SUMMARY=$(git log --since="$SINCE" --oneline -- 'openapi/' || true) - echo "change_summary<> $GITHUB_OUTPUT - echo "$SUMMARY" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - fi - - sync-docs: - needs: detect-changes - if: needs.detect-changes.outputs.has_changes == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Sync documentation with Claude - id: claude - uses: anthropics/claude-code-action@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - claude_args: - --allowedTools Bash,Read,Write,Edit,Glob,Grep,WebFetch - --model claude-opus-4-5-20251101 - --max-turns 30 - prompt: | - ## Task: Documentation Sync Review - - OpenAPI schema changes were detected in the last 24 hours. Your task is to ensure all documentation is up to date with these changes. - - ### Recent Schema Changes - **Commits:** - ${{ needs.detect-changes.outputs.change_summary }} - - **Changed Files:** - ${{ needs.detect-changes.outputs.changed_files }} - - ### Review Checklist - - 1. **Understand the Changes** - - Read the changed files in `openapi/` to understand what was modified - - Note any new endpoints, modified request/response schemas, or removed functionality - - 2. **Check Schema Examples** - - Review example requests and responses in `openapi/paths/` YAML files - - Review examples in `openapi/components/schemas/` YAML files - - Ensure examples match the current schema definitions - - 3. **Check Mintlify Documentation** - - Scan `mintlify/` for guides and tutorials that reference the changed endpoints or schemas - - Check code samples in MDX files for accuracy - - Look for flow descriptions that may be outdated - - Key areas to check: - - `mintlify/payouts-and-b2b/` - Payouts documentation - - `mintlify/ramps/` - Crypto ramp documentation - - `mintlify/rewards/` - Rewards documentation - - `mintlify/global-p2p/` - P2P/remittance documentation - - `mintlify/snippets/` - Shared content snippets - - `mintlify/platform-overview/` - Core concepts - - 4. **Check for Outdated References** - - Look for hardcoded field names that may have changed - - Check for endpoint paths that may have been renamed - - Verify response structure descriptions match the schema - - ### Actions - - If documentation updates are needed: - 1. Make the necessary changes to keep docs in sync - 2. Run `npm run build:openapi` if you modified any files in `openapi/` - 3. Create a PR with: - - Branch name: `docs/sync-$(date +%Y%m%d)` - - Clear title describing the sync - - Description listing what was updated and why - - If documentation is already up to date: - - Output a brief summary confirming no updates needed - - Do not create a PR - - ### Guidelines - - Follow the documentation standards in `mintlify/CLAUDE.md` - - Use snippets from `mintlify/snippets/` to avoid duplication - - Keep examples consistent with actual API behavior - - Do not add unnecessary comments or over-explain code - - - name: Notify Slack on PR creation - if: success() && steps.claude.outputs.pr_url - uses: slackapi/slack-github-action@v2.0.0 - with: - webhook: ${{ secrets.SLACK_WEBHOOK_ENGGRID }} - webhook-type: incoming-webhook - payload: | - { - "text": "Documentation Sync: PR Created", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Documentation Sync: PR Created*\n\nOpenAPI schema changes were detected and documentation updates have been proposed.\n\n*Pull Request:* <${{ steps.claude.outputs.pr_url }}|View PR>\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>" - } - } - ] - } diff --git a/.github/workflows/dummy.yml b/.github/workflows/dummy.yml deleted file mode 100644 index f2b2d687..00000000 --- a/.github/workflows/dummy.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Dummy - -on: - pull_request: - paths-ignore: - - 'openapi.yaml' - - 'mintlify/**' - - '.markdownlint.json' - merge_group: - paths-ignore: - - 'openapi.yaml' - - 'mintlify/**' - - '.markdownlint.json' - -jobs: - build: - name: Build OpenAPI Documentation - runs-on: ubuntu-24.04 - steps: - - run: echo "Skipping Build OpenAPI Documentation - no relevant changes" - - lint: - name: Lint Code & Documentation - runs-on: ubuntu-24.04 - steps: - - run: echo "Skipping Lint Code & Documentation - no relevant changes" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index bae66112..00000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Lint - -on: - push: - branches: [ main ] - paths: - - 'openapi.yaml' - - '.markdownlint.json' - - 'mintlify/**' - pull_request: - branches: [ main ] - paths: - - 'openapi.yaml' - - '.markdownlint.json' - - 'mintlify/**' - # Allow manual triggering - workflow_dispatch: - -jobs: - lint: - name: Lint Code & Documentation - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Install mint - run: npm i -g mint - - - name: Lint - run: make lint \ No newline at end of file diff --git a/.github/workflows/openapi-build.yml b/.github/workflows/openapi-build.yml deleted file mode 100644 index f75a018d..00000000 --- a/.github/workflows/openapi-build.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: OpenAPI Documentation Build - -on: - push: - branches: [ main ] - paths: - - 'openapi.yaml' - - 'mintlify/**' - pull_request: - branches: [ main ] - paths: - - 'openapi.yaml' - - 'mintlify/**' - # Allow manual triggering - workflow_dispatch: - -jobs: - build: - name: Build OpenAPI Documentation - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '24' - cache: 'npm' - cache-dependency-path: | - package-lock.json - - - name: Install root dependencies - run: npm ci - - - name: Build documentation - run: make build diff --git a/.github/workflows/stainless-action.yml b/.github/workflows/stainless-action.yml deleted file mode 100644 index 0de4a72b..00000000 --- a/.github/workflows/stainless-action.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Build SDKs for pull request - -env: - OAS_PATH: ./openapi.yaml - STAINLESS_ORG: lightspark - STAINLESS_PROJECT: grid - -on: - pull_request: - types: - - opened - - synchronize - - reopened - - closed - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - preview: - if: github.event.action != 'closed' - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 2 - - - name: Run preview builds - uses: stainless-api/upload-openapi-spec-action/preview@v1 - with: - org: ${{ env.STAINLESS_ORG }} - project: ${{ env.STAINLESS_PROJECT }} - oas_path: ${{ env.OAS_PATH }} - - merge: - if: github.event.action == 'closed' && github.event.pull_request.merged == true - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 2 - - - name: Run merge build - uses: stainless-api/upload-openapi-spec-action/merge@v1 - with: - org: ${{ env.STAINLESS_ORG }} - project: ${{ env.STAINLESS_PROJECT }} - oas_path: ${{ env.OAS_PATH }} diff --git a/.gitignore b/.gitignore index 777b53b5..b1346e6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,7 @@ -# Dependencies -node_modules/ - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Editor directories and files +.prism.log +.gradle .idea -.vscode -*.swp -*.swo -.DS_Store - - -# Gradle -.gradle/ +.kotlin build/ -gradle-app.setting -!gradle-wrapper.jar -!gradle-wrapper.properties -.gradletasknamecache -bin/ - -# Local Environment files -.env -local.properties - -# Grid CLI credentials -.grid-credentials -.claude/settings.local.json - -# CLI build output -cli/dist/ - -# Figma design tokens (local reference only) -mintlify/tokens/ - +codegen.log +kls_database.db diff --git a/.markdownlint.json b/.markdownlint.json deleted file mode 100644 index 0d1cdf15..00000000 --- a/.markdownlint.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "default": true, - "MD013": false, - "MD033": false, - "MD041": false, - "MD029": false, - "MD046": false, - "MD024": false -} \ No newline at end of file diff --git a/.redocly.lint-ignore.yaml b/.redocly.lint-ignore.yaml deleted file mode 100644 index 5a215899..00000000 --- a/.redocly.lint-ignore.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API. -# See https://redocly.com/docs/cli/ for more information. -openapi.yaml: - no-unused-components: - - '#/components/schemas/AllErrors' - no-required-schema-properties-undefined: - - '#/components/schemas/QuoteSource/required/0' - - '#/components/schemas/Quote/properties/destination/required/0' - no-invalid-media-type-examples: - - >- - #/paths/~1customers~1external-accounts/post/requestBody/content/application~1json/schema - - >- - #/paths/~1platform~1external-accounts/post/requestBody/content/application~1json/schema - - '#/paths/~1quotes/post/requestBody/content/application~1json/schema' diff --git a/.redocly.yaml b/.redocly.yaml deleted file mode 100644 index d1b9ed58..00000000 --- a/.redocly.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apis: - grid@v1: - root: ./openapi.yaml - -extends: - - recommended - -rules: - operation-description: error - operation-operationId: error - operation-2xx-response: error - security-defined: error - boolean-parameter-prefixes: error - no-unused-components: error - no-unresolved-refs: error - no-required-schema-properties-undefined: error - tag-description: off - tags-alphabetical: off - paths-kebab-case: warn - no-ambiguous-paths: error - info-license: warn - -theme: - openapi: - theme: - colors: - primary: - main: "#2563EB" - typography: - headings: - fontFamily: 'Roboto, sans-serif' \ No newline at end of file diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml deleted file mode 100644 index 5817a8ab..00000000 --- a/.stainless/stainless.yml +++ /dev/null @@ -1,341 +0,0 @@ -# yaml-language-server: $schema=https://app.stainless.com/config.schema.json - -# The main edition for the config, see the [docs] for more information. -# -# [docs]: https://www.stainless.com/docs/reference/editions -edition: 2025-10-10 - -organization: - # Name of your organization or company, used to determine the name of the client - # and headings. - name: grid - # Link to your API documentation. - docs: '' - # Contact email for bug reports, questions, and support requests. - contact: support@lightspark.com - -# `targets` define the output targets and their customization options, such as -# whether to emit the Node SDK and what its package name should be. -targets: - typescript: - # The edition for this target, see the [docs] for more information. - # - # [docs]: https://www.stainless.com/docs/reference/editions - edition: typescript.2025-10-10 - package_name: grid - production_repo: null - publish: - npm: false - kotlin: - edition: kotlin.2025-10-08 - reverse_domain: com.grid.api - production_repo: null - publish: - maven: false - -# `environments` are a map of the name of the environment (e.g. "sandbox", -# "production") to the corresponding url to use. -environments: - production: https://api.lightspark.com/grid/2025-10-13 - -# `resources` define the structure and organization for your API, such as how -# methods and models are grouped together and accessed. See the [configuration -# guide] for more information. -# -# [configuration guide]: https://www.stainless.com/docs/guides/configure#resources -resources: - config: - # Configure the methods defined in this resource. Each key in the object is the - # name of the method and the value is either an endpoint (for example, `get /foo`) - # or an object with more detail. - # - # [reference]: https://www.stainless.com/docs/reference/config#method - # Configure the models--named types--defined in the resource. Each key in the - # object is the name of the model and the value is either the name of a schema in - # `#/components/schemas` or an object with more detail. - # - # [reference]: https://www.stainless.com/docs/reference/config#model - models: - customer_info_field_name: '#/components/schemas/CustomerInfoFieldName' - platform_currency_config: '#/components/schemas/PlatformCurrencyConfig' - platform_config: '#/components/schemas/PlatformConfig' - methods: - retrieve: get /config - update: patch /config - - customers: - models: - customer_type: '#/components/schemas/CustomerType' - individual_customer: '#/components/schemas/IndividualCustomer' - customer: '#/components/schemas/Customer' - address: '#/components/schemas/Address' - ultimate_beneficial_owner: '#/components/schemas/UltimateBeneficialOwner' - business_customer: '#/components/schemas/BusinessCustomer' - business_customer_fields: "#/components/schemas/BusinessCustomerFields" - business_info: "#/components/schemas/BusinessInfo" - individual_customer_fields: "#/components/schemas/IndividualCustomerFields" - customer_one_of: "#/components/schemas/CustomerOneOf" - customer_create: "#/components/schemas/CustomerCreateRequest" - customer_update: "#/components/schemas/CustomerUpdateRequest" - methods: - create: - endpoint: post /customers - body_param_name: CreateCustomerRequest - list: get /customers - retrieve: get /customers/{customerId} - update: - endpoint: patch /customers/{customerId} - body_param_name: UpdateCustomerRequest - delete: delete /customers/{customerId} - get_kyc_link: get /customers/kyc-link - list_internal_accounts: get /customers/internal-accounts - # Subresources define resources that are nested within another for more powerful - # logical groupings, e.g. `cards.payments`. - subresources: - external_accounts: - models: - us_account_info: '#/components/schemas/UsAccountInfo' - pix_account_info: '#/components/schemas/PixAccountInfo' - iban_account_info: '#/components/schemas/IbanAccountInfo' - upi_account_info: '#/components/schemas/UpiAccountInfo' - spark_wallet_info: '#/components/schemas/SparkWalletInfo' - solana_wallet_info: '#/components/schemas/SolanaWalletInfo' - tron_wallet_info: '#/components/schemas/TronWalletInfo' - polygon_wallet_info: '#/components/schemas/PolygonWalletInfo' - clabe_account_info: '#/components/schemas/ClabeAccountInfo' - base_wallet_info: '#/components/schemas/BaseWalletInfo' - individual_beneficiary: '#/components/schemas/IndividualBeneficiary' - business_beneficiary: '#/components/schemas/BusinessBeneficiary' - external_account: '#/components/schemas/ExternalAccount' - external_account_create: '#/components/schemas/ExternalAccountCreateRequest' - lightning_external_account_info: "#/components/schemas/LightningExternalAccountInfo" - ngn_account_external_account_info: "#/components/schemas/NgnAccountExternalAccountInfo" - base_external_account_info: "#/components/schemas/BaseExternalAccountInfo" - base_beneficiary: "#/components/schemas/BaseBeneficiary" - beneficiary_one_of: "#/components/schemas/BeneficiaryOneOf" - external_account_info_one_of: "#/components/schemas/ExternalAccountInfoOneOf" - methods: - list: get /customers/external-accounts - create: post /customers/external-accounts - bulk: - methods: - upload_csv: post /customers/bulk/csv - get_job_status: get /customers/bulk/jobs/{jobId} - - platform: - methods: - list_internal_accounts: - endpoint: get /platform/internal-accounts - paginated: false - subresources: - external_accounts: - methods: - list: - endpoint: get /platform/external-accounts - paginated: false - create: post /platform/external-accounts - - plaid: - methods: - create_link_token: post /plaid/link-tokens - submit_public_token: post /plaid/callback/{plaid_link_token} - - transfer_in: - models: - transaction: '#/components/schemas/Transaction' - base_transaction_destination: "#/components/schemas/BaseTransactionDestination" - methods: - create: post /transfer-in - - transfer_out: - methods: - create: post /transfer-out - - receiver: - models: - counterparty_field_definition: '#/components/schemas/CounterpartyFieldDefinition' - lookup_response: '#/components/schemas/ReceiverLookupResponse' - methods: - lookup_uma: get /receiver/uma/{receiverUmaAddress} - lookup_external_account: get /receiver/external-account/{accountId} - - quotes: - models: - currency: '#/components/schemas/Currency' - payment_instructions: '#/components/schemas/PaymentInstructions' - outgoing_rate_details: '#/components/schemas/OutgoingRateDetails' - quote: '#/components/schemas/Quote' - base_payment_account_info: "#/components/schemas/BasePaymentAccountInfo" - base_quote_source: "#/components/schemas/BaseQuoteSource" - quote_source_one_of: "#/components/schemas/QuoteSourceOneOf" - base_destination: "#/components/schemas/BaseDestination" - quote_destination_one_of: "#/components/schemas/QuoteDestinationOneOf" - methods: - retrieve: get /quotes/{quoteId} - create: post /quotes - list: get /quotes - execute: post /quotes/{quoteId}/execute - - transactions: - models: - transaction_type: '#/components/schemas/TransactionType' - incoming_transaction: '#/components/schemas/IncomingTransaction' - transaction_status: '#/components/schemas/TransactionStatus' - base_transaction_source: "#/components/schemas/BaseTransactionSource" - transaction_source_one_of: "#/components/schemas/TransactionSourceOneOf" - methods: - list: get /transactions - retrieve: get /transactions/{transactionId} - approve: post /transactions/{transactionId}/approve - reject: post /transactions/{transactionId}/reject - - webhooks: - methods: - send_test: post /webhooks/test - - invitations: - models: - currency_amount: '#/components/schemas/CurrencyAmount' - uma_invitation: '#/components/schemas/UmaInvitation' - methods: - create: post /invitations - retrieve: get /invitations/{invitationCode} - claim: post /invitations/{invitationCode}/claim - cancel: post /invitations/{invitationCode}/cancel - - sandbox: - methods: - send_funds: post /sandbox/send - subresources: - uma: - methods: - receive_payment: post /sandbox/uma/receive - internal_accounts: - models: - internal_account: '#/components/schemas/InternalAccount' - methods: - fund: post /sandbox/internal-accounts/{accountId}/fund - - uma_providers: - methods: - list: get /uma-providers - - tokens: - models: - permission: '#/components/schemas/Permission' - api_token: '#/components/schemas/ApiToken' - methods: - create: post /tokens - list: get /tokens - retrieve: get /tokens/{tokenId} - delete: delete /tokens/{tokenId} - -settings: - # All generated integration tests that hit the prism mock http server are marked - # as skipped. Removing this setting or setting it to false enables tests, but - # doing so may result in test failures due to bugs in the test server. - # - # [prism mock http server]: https://stoplight.io/open-source/prism - disable_mock_tests: true - license: Apache-2.0 - -# `client_settings` define settings for the API client, such as extra constructor -# arguments (used for authentication), retry behavior, idempotency, etc. -client_settings: - opts: - username: - type: string - nullable: false - auth: - security_scheme: BasicAuth - role: username - description: API token authentication using format `:` - read_env: GRID_USERNAME - password: - type: string - nullable: false - auth: - security_scheme: BasicAuth - role: password - description: API token authentication using format `:` - read_env: GRID_PASSWORD - webhook_signature: - type: string - nullable: true - auth: - security_scheme: WebhookSignature - description: | - Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to verify that the webhook was sent by Grid. - - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - read_env: GRID_WEBHOOK_SIGNATURE - -# `readme` is used to configure the code snippets that will be rendered in the -# README.md of various SDKs. In particular, you can change the `headline` -# snippet's endpoint and the arguments to call it with. -readme: - example_requests: - default: - type: request - endpoint: get /config - params: {} - headline: - type: request - endpoint: get /config - params: {} - pagination: - type: request - endpoint: get /customers - params: {} - -pagination: - - name: default_pagination - type: cursor - request: - cursor: - type: string - x-stainless-pagination-property: - purpose: next_cursor_param - response: - data: - type: array - items: - type: object - nextCursor: - type: string - x-stainless-pagination-property: - purpose: next_cursor_field - hasMore: - type: boolean - x-stainless-pagination-property: - purpose: has_next_page - totalCount: - type: integer - -openapi: - code_samples: mintlify - transformations: - - command: splitSchemasByEnumProperty - reason: Split error schemas with multiple enum values into distinct ones - args: - unionPath: AllErrors - enumProperty: code - -errors: - union: - source: AllErrors - discriminator: code - status_property: status - -codeflow: - detect_breaking_changes: true -diagnostics: - ignored: - Schema/ObjectHasNoProperties: - - pagination.0.response.data.items diff --git a/.stainless/workspace.json b/.stainless/workspace.json deleted file mode 100644 index 1af98546..00000000 --- a/.stainless/workspace.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "project": "grid", - "openapi_spec": "../mintlify/openapi.yaml", - "stainless_config": "stainless.yml", - "targets": { - "typescript": { - "output_path": "../sdks/grid-typescript" - } - } -} diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 00000000..8f102128 --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 44 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lightspark%2Fgrid-e951665c552dd2e74f6c8e51407409a787d01c88e6d3bdaf42b776c4dda290c8.yml +openapi_spec_hash: e790ee4f8480260dbc6043a92f652022 +config_hash: ae718d4f8ed39c8caa45d820073d0b47 diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 78285835..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,173 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -This is the **Grid API** documentation repository. Grid is an API that enables modern financial institutions to send and receive global payments across fiat, stablecoins, and Bitcoin. The repository contains: - -1. **OpenAPI specification** (split into modular YAML files) -2. **Mintlify documentation** (MDX files with guides, tutorials, and API reference) - -This is a documentation-only repository - there is no application code. - -## Common Commands - -### Building and Linting - -```bash -# Install dependencies -npm install -# or -make install - -# Build OpenAPI schema (bundles split files into single openapi.yaml) -npm run build:openapi -# or -make build - -# Lint OpenAPI schema and markdown files -npm run lint -# or -make lint - -# Lint only OpenAPI -npm run lint:openapi -# or -make lint-openapi - -# Lint only markdown -npm run lint:markdown -# or -make lint-markdown -``` - -### Documentation Development - -```bash -# Serve Mintlify documentation locally (requires mint CLI installed globally) -cd mintlify && mint dev -# or -make mint -``` - -## Architecture - -### OpenAPI Schema Structure - -The OpenAPI specification uses a **split-file architecture** managed by Redocly: - -- **Source files**: `openapi/` directory contains modular YAML files - - `openapi/openapi.yaml` - Root specification with references - - `openapi/paths/` - Endpoint definitions organized by domain - - `openapi/components/schemas/` - Reusable schema definitions - - `openapi/webhooks/` - Webhook event definitions - -- **Built file**: `openapi.yaml` - Bundled specification at repository root (also copied to `mintlify/openapi.yaml`) - -**Important**: When editing OpenAPI specs, edit files in `openapi/` directory, then run `npm run build:openapi` to bundle. The root `openapi.yaml` is generated and should not be edited directly. - -### Domain Organization - -The API is organized into four main use cases, reflected in both the OpenAPI paths and Mintlify docs: - -1. **Payouts** - Send value instantly across currencies and borders - - Customer management (`/customers`) - - Internal accounts (`/customers/internal-accounts`, `/platform/internal-accounts`) - - External accounts (`/customers/external-accounts`, `/platform/external-accounts`) - - Quotes and transactions - -2. **Ramps** - Convert between crypto and fiat - - Customer onboarding with KYC - - Plaid integration for bank account linking - - Fiat-to-crypto and crypto-to-fiat conversion flows - - Self-custody wallet support - -3. **Rewards & Cashback** - Deliver micro-payouts at scale - - Similar structure to Payouts - - Optimized for high-volume, low-value transactions - -4. **Global P2P (Remittances)** - Accept funds via bank transfers, wallets, or UMAs - - User management with UMA addresses (`/users` endpoints in actual API) - - UMA address resolution (`/receiver/uma/{receiverUmaAddress}`) - - Payment approval/rejection flows - - Invitations system (`/invitations`) - -### Key Concepts - -- **Customers**: End users of the platform (used in Payouts, Ramps, Rewards) -- **Users**: Distinction unclear from docs, but appears related to UMA-based flows -- **Internal Accounts**: Platform-managed accounts for holding funds -- **External Accounts**: Bank accounts connected for deposits/withdrawals -- **Quotes**: Time-limited exchange rate locks for cross-currency transactions -- **Transactions**: Payment records (incoming/outgoing) -- **UMA Addresses**: Universal Money Addresses (e.g., `$alice@example.com`) for P2P payments - -### Mintlify Documentation Structure - -- `mintlify/docs.json` - Navigation configuration with tabs for each use case -- `mintlify/index.mdx` - Landing page -- `mintlify/{use-case}/` - Use case-specific documentation - - `index.mdx` - Use case overview - - `quickstart.mdx` - Quick start guide - - `onboarding/` or `developer-guides/` - Implementation guides - - `accounts/`, `payment-flow/`, etc. - Topic-specific guides -- `mintlify/snippets/` - Reusable MDX snippets (imported into multiple docs) -- `mintlify/api-reference/` - API authentication and environment docs -- `mintlify/developer-resources/` - SDKs, tools, Postman collections -- `mintlify/changelog.mdx` - API changelog - -### Shared Documentation Patterns - -The repository uses **MDX snippets** to avoid duplication across use cases. Common snippets in `mintlify/snippets/`: - -- `platform-config-currency-api-webhooks.mdx` - Platform configuration -- `internal-accounts.mdx` - Internal account management -- `external-accounts.mdx` - External account management -- `webhooks.mdx` - Webhook setup and verification -- `kyc-onboarding.mdx` - KYC onboarding process -- `plaid-integration.mdx` - Plaid integration -- `terminology.mdx` - Terminology definitions - -Import these snippets rather than duplicating content. - -## Authentication - -The API uses HTTP Basic Authentication with format `:` (Base64 encoded). All endpoints require authentication except the webhook endpoints (which use signature verification instead). - -Webhooks use **P-256 ECDSA signatures** in the `X-Grid-Signature` header for verification. - -## Important Notes - -### OpenAPI Development - -- Use Redocly for bundling and linting: `@redocly/cli` -- Configuration in `.redocly.yaml` -- Lint rules include operation descriptions, operation IDs, security definitions -- Always run `npm run lint:openapi` before committing OpenAPI changes - -### Mintlify Development - -- MDX files must include frontmatter with `title` and `description` -- Follow the writing standards in `mintlify/CLAUDE.md` -- Use second-person voice ("you") -- Test all code examples -- Use relative paths for internal links -- The mintlify subdirectory has its own CLAUDE.md with additional guidance - -### Documentation Philosophy - -- **Document just enough** for user success - balance between too much and too little -- **Avoid duplication** - use snippets for shared content across use cases -- **Make content evergreen** when possible -- **Check existing patterns** for consistency before making changes -- **Search before adding** - look for existing information before creating new content - -## Environments - -- **Production**: `https://api.lightspark.com/grid/2025-10-13` -- **Sandbox**: Available for testing (see sandbox endpoints `/sandbox/send`, `/sandbox/receive`) - -## Support - -For questions or issues: support@lightspark.com diff --git a/LICENSE b/LICENSE index 2c224b91..fdf4db6c 100644 --- a/LICENSE +++ b/LICENSE @@ -2,180 +2,180 @@ Version 2.0, January 2004 http://www.apache.org/licenses/ -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" @@ -186,16 +186,16 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2023 Lightspark Group, Inc. + Copyright 2026 Grid -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile deleted file mode 100644 index 053a41a4..00000000 --- a/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -.PHONY: install build build-openapi mint lint lint-openapi lint-markdown cli-install cli-build cli - -install: - npm install - npm i -g mint - -build: - npm run build:openapi - -build-openapi: - npm run build:openapi - -mint: - cd mintlify && mint dev - -lint: - npm run lint - cd mintlify && mint openapi-check openapi.yaml - -lint-openapi: - npm run lint:openapi - -lint-markdown: - npm run lint:markdown - -cli-install: - cd cli && npm install - -cli-build: - cd cli && npm run build - -cli: - cd cli && npm run dev -- \ No newline at end of file diff --git a/README.md b/README.md index 114b6dbc..a7e65683 100644 --- a/README.md +++ b/README.md @@ -1,929 +1,740 @@ -# Grid API +# Grid Kotlin API Library -Grid is an API that enables modern financial institutions to easily send and receive global payments. One API to send, receive, and settle value globally—fiat, stablecoins, or Bitcoin. Always real time, always low cost, built on an open network. +[![Maven Central](https://img.shields.io/maven-central/v/com.grid.api/grid-kotlin)](https://central.sonatype.com/artifact/com.grid.api/grid-kotlin/0.0.1) +[![javadoc](https://javadoc.io/badge2/com.grid.api/grid-kotlin/0.0.1/javadoc.svg)](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.0.1) -See the full documentation at . +The Grid Kotlin SDK provides convenient access to the [Grid REST API](grid.lightspark.com) from applications written in Kotlin. -## Overview +It is generated with [Stainless](https://www.stainless.com/). -The Grid API provides endpoints for: +KDocs are available on [javadoc.io](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.0.1). -1. **Payouts** - Send value instantly across currencies and borders -2. **Ramps** - Convert between crypto and fiat currencies -3. **Rewards & Cashback** - Deliver micro-payouts at scale instantly and in local currency -4. **Global P2P** - Accept funds via bank transfers, wallets, or UMAs, and settle them in the currency of your choice +## Installation -### Key Features +### Gradle -- **Single API, global reach**: Grid interacts with the Money Grid to route your payments globally -- **No crypto handling**: Grid converts between fiat and crypto for you on demand to simplify your implementation and minimize FX costs -- **Real-time settlement**: Leverages local instant banking rails and global low latency crypto rails to settle payments in real-time +```kotlin +implementation("com.grid.api:grid-kotlin:0.0.1") +``` + +### Maven + +```xml + + com.grid.api + grid-kotlin + 0.0.1 + +``` -## Authentication +## Requirements -All API requests must include HTTP Basic Authentication using the `Authorization` header. The credentials should be provided in the format `:` and then Base64 encoded. +This library requires Java 8 or later. -Example: +## Usage -```http -Authorization: Basic +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams + +// Configures using the `grid.username`, `grid.password`, `grid.webhookSignature` and `grid.baseUrl` system properties +// Or configures using the `GRID_USERNAME`, `GRID_PASSWORD`, `GRID_WEBHOOK_SIGNATURE` and `GRID_BASE_URL` environment variables +val client: GridClient = GridOkHttpClient.fromEnv() + +val params: QuoteCreateParams = QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() +val quote: Quote = client.quotes().create(params) ``` -Where `` is the Base64 encoding of `:`. +## Client configuration -You can generate a new API token and client secret at any time in the Grid dashboard. +Configure the client using system properties or environment variables: -## API Documentation +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient -The API is documented using the OpenAPI 3.1 specification. The full schema is available in the `openapi.yaml` file in this repository. +// Configures using the `grid.username`, `grid.password`, `grid.webhookSignature` and `grid.baseUrl` system properties +// Or configures using the `GRID_USERNAME`, `GRID_PASSWORD`, `GRID_WEBHOOK_SIGNATURE` and `GRID_BASE_URL` environment variables +val client: GridClient = GridOkHttpClient.fromEnv() +``` -### Documentation Format +Or manually: -You can view the API documentation in two formats: +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient -1. **Live Documentation Server**: Use `mint dev` to start a local documentation server for the Mintlify docs -2. **Markdown Documentation**: Use `npm run build:markdown` to generate markdown documentation in `generated/api-docs.md` +val client: GridClient = GridOkHttpClient.builder() + .username("My Username") + .password("My Password") + .build() +``` -## API Guides +Or using a combination of the two approaches: -We provide detailed guides for common workflows with the Grid API: +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient -- **Payouts**: Send value instantly across currencies and borders -- **Ramps**: Convert between crypto and fiat currencies -- **Rewards & Cashback**: Deliver micro-payouts at scale instantly and in local currency -- **Global P2P**: Accept funds via bank transfers, wallets, or UMAs, and settle them in the currency of your choice +val client: GridClient = GridOkHttpClient.builder() + // Configures using the `grid.username`, `grid.password`, `grid.webhookSignature` and `grid.baseUrl` system properties + // Or configures using the `GRID_USERNAME`, `GRID_PASSWORD`, `GRID_WEBHOOK_SIGNATURE` and `GRID_BASE_URL` environment variables + .fromEnv() + .username("My Username") + .build() +``` -For detailed implementation guides, see the [Mintlify documentation](./mintlify/) which includes: +See this table for the available options: -- Platform configuration and setup -- User and customer management -- Payment flows and reconciliation -- Webhook security and verification -- Sandbox testing environments +| Setter | System property | Environment variable | Required | Default value | +| ------------------ | ----------------------- | ------------------------ | -------- | ---------------------------------------------- | +| `username` | `grid.username` | `GRID_USERNAME` | true | - | +| `password` | `grid.password` | `GRID_PASSWORD` | true | - | +| `webhookSignature` | `grid.webhookSignature` | `GRID_WEBHOOK_SIGNATURE` | false | - | +| `baseUrl` | `grid.baseUrl` | `GRID_BASE_URL` | true | `"https://api.lightspark.com/grid/2025-10-13"` | -## Key Endpoints +System properties take precedence over environment variables. -The Grid API provides endpoints across four main use cases: +> [!TIP] +> Don't create more than one client in the same application. Each client has a connection pool and +> thread pools, which are more efficient to share between requests. -### Payouts +### Modifying configuration -- **Customer Management** - - `POST /customers` - Create a new customer - - `PATCH /customers/{customerId}` - Update a customer by ID - - `GET /customers/{customerId}` - Get a customer by ID -- **Account Management** - - `POST /internal-accounts` - Create internal accounts - - `POST /external-accounts` - Connect external bank accounts -- **Payment Processing** - - `POST /quotes` - Create payment quotes - - `GET /quotes/{quoteId}` - Get quote details - - `POST /transactions` - Execute payments - - `GET /transactions/{transactionId}` - Get transaction status +To temporarily use a modified client configuration, while reusing the same connection and thread pools, call `withOptions()` on any client or service: -### Global P2P (Remittances) +```kotlin +import com.grid.api.client.GridClient -- **User Management** - - `POST /users` - Add a new user with UMA address - - `PATCH /users/{userId}` - Update a user by ID - - `GET /users/{userId}` - Get a user by ID -- **UMA Address Resolution** - - `GET /receiver/{receiverUmaAddress}` - Get receiver information and supported currencies -- **Payment Processing** - - `POST /quotes` - Create payment quotes - - `GET /quotes/{quoteId}` - Get quote details - - `POST /transactions/{transactionId}/approve` - Approve incoming payments - - `POST /transactions/{transactionId}/reject` - Reject incoming payments +val clientWithOptions: GridClient = client.withOptions { + it.baseUrl("https://example.com") + it.maxRetries(42) +} +``` -### Platform Configuration +The `withOptions()` method does not affect the original client or service. -- `GET /config` - Get platform configuration -- `PATCH /config` - Update platform configuration +## Requests and responses -### Webhooks +To send a request to the Grid API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a Kotlin class. -- `OUTGOING_PAYMENT` - Notify when a payment is sent -- `INCOMING_PAYMENT` - Notify when a payment is pending, completed, or failed +For example, `client.quotes().create(...)` should be called with an instance of `QuoteCreateParams`, and it will return an instance of `Quote`. -## Quick-Start Guides +## Immutability -### Sending Payments (Outgoing Flow) +Each class in the SDK has an associated [builder](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java) or factory method for constructing it. -This guide outlines the process for platforms to send payments using the Grid API. +Each class is [immutable](https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html) once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy. -#### Process Overview +Because each class is immutable, builder modification will _never_ affect already built class instances. -The following sequence diagram illustrates the interaction between your platform and the Grid API when sending payments: +## Asynchronous execution -```mermaid -sequenceDiagram - participant Client as Your Platform - participant Grid as Grid API - participant Bank as Banking Provider - - Client->>Grid: GET /receiver/{umaAddress} - Grid-->>Client: Supported currencies and requirements - Note over Client: Select currency and amount - Client->>Grid: POST /quotes - Grid-->>Client: Quote with payment instructions - Note over Client: Execute payment using instructions - Client->>Bank: Initiate bank transfer with reference - - opt Payment Status Polling - loop Until completed or failed - Client->>Grid: GET /quotes/{quoteId} - Grid-->>Client: Quote with current status - end - end - - Grid->>Client: Webhook: OUTGOING_PAYMENT (status update) - Client-->>Grid: HTTP 200 OK (acknowledge webhook) -``` - -The process consists of five main steps: - -1. **Look up the recipient's UMA address** to validate it and retrieve supported currencies -2. **Create a payment quote** to lock in exchange rates and get payment instructions -3. **Execute the payment** through your banking provider using the instructions -4. **Track the payment status** by polling or waiting for a webhook -5. **Receive completion notification** when the payment completes or fails - -#### Step 1: Look up recipient UMA address - -First, check if an UMA address is valid and retrieve supported currencies and exchange rates. - -```http -GET /receiver/$recipient@example.com?userId=9f84e0c2a72c4fa -``` - -Response: - -```json -{ - "receivingUmaAddress": "$recipient@example.com", - "supportedCurrencies": [ - { - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "estimatedExchangeRate": 1.0, - "min": 100, - "max": 10000000 - }, - { - "currency": { - "code": "EUR", - "name": "Euro", - "symbol": "€", - "decimals": 2 - }, - "estimatedExchangeRate": 0.92, - "min": 100, - "max": 9000000 - } - ], - "requiredPayerDataFields": [ - { - "name": "FULL_NAME", - "mandatory": true - }, - { - "name": "BIRTH_DATE", - "mandatory": true - } - ], - "lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009" -} +The default client is synchronous. To switch to asynchronous execution, call the `async()` method: + +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams + +// Configures using the `grid.username`, `grid.password`, `grid.webhookSignature` and `grid.baseUrl` system properties +// Or configures using the `GRID_USERNAME`, `GRID_PASSWORD`, `GRID_WEBHOOK_SIGNATURE` and `GRID_BASE_URL` environment variables +val client: GridClient = GridOkHttpClient.fromEnv() + +val params: QuoteCreateParams = QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() +val quote: Quote = client.async().quotes().create(params) ``` -#### Step 2: Create a payment quote +Or create an asynchronous client from the beginning: + +```kotlin +import com.grid.api.client.GridClientAsync +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams + +// Configures using the `grid.username`, `grid.password`, `grid.webhookSignature` and `grid.baseUrl` system properties +// Or configures using the `GRID_USERNAME`, `GRID_PASSWORD`, `GRID_WEBHOOK_SIGNATURE` and `GRID_BASE_URL` environment variables +val client: GridClientAsync = GridOkHttpClientAsync.fromEnv() + +val params: QuoteCreateParams = QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() +val quote: Quote = client.quotes().create(params) +``` + +The asynchronous client supports the same options as the synchronous one, except most methods are [suspending](https://kotlinlang.org/docs/coroutines-guide.html). + +## File uploads + +The SDK defines methods that accept files. -Generate a quote for the payment with locked exchange rates and fees. +To upload a file, pass a [`Path`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html): -```http -POST /quotes +```kotlin +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse +import java.nio.file.Paths + +val params: BulkUploadCsvParams = BulkUploadCsvParams.builder() + .file(Paths.get("/path/to/file")) + .build() +val response: BulkUploadCsvResponse = client.customers().bulk().uploadCsv(params) ``` -Request body: +Or an arbitrary [`InputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html): -```json -{ - "lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009", - "sendingCurrencyCode": "USD", - "receivingCurrencyCode": "EUR", - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Invoice #1234 payment" -} +```kotlin +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse +import java.net.URL + +val params: BulkUploadCsvParams = BulkUploadCsvParams.builder() + .file(URL("https://example.com//path/to/file").openStream()) + .build() +val response: BulkUploadCsvResponse = client.customers().bulk().uploadCsv(params) ``` -Response: - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "EUR", - "name": "Euro", - "symbol": "€", - "decimals": 2 - }, - "totalSendingAmount": 10100, - "totalReceivingAmount": 9200, - "exchangeRate": 0.92, - "expiresAt": "2023-09-01T14:30:00Z", - "feesIncluded": 100, - "counterpartyInformation": { - "FULL_NAME": "Jane Receiver", - "BIRTH_DATE": "1990-01-01" - }, - "paymentInstructions": [ - { - "instructionsNotes": "Include reference code in transfer memo", - "accountOrWalletInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "1234567890", - "routingNumber": "021000021", - "bankName": "Chase Bank", - "referenceCode": "REF123456" - } - } - ] -} +Or a `ByteArray`: + +```kotlin +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse + +val params: BulkUploadCsvParams = BulkUploadCsvParams.builder() + .file("content".toByteArray()) + .build() +val response: BulkUploadCsvResponse = client.customers().bulk().uploadCsv(params) ``` -#### Step 3: Execute the payment - -Use the `paymentInstructions` from the quote to execute a payment through your banking provider. Be sure to include the provided reference code if applicable. - -#### Step 4: Track payment status - -You can track the status of your payment by polling the payment status endpoint or waiting for the webhook notification. To poll the payment status, use the following endpoint: - -```http -GET /quotes/Quote:019542f5-b3e7-1d02-0000-000000000006 -``` - -Response: - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "EUR", - "name": "Euro", - "symbol": "€", - "decimals": 2 - }, - "totalSendingAmount": 10100, - "totalReceivingAmount": 9200, - "exchangeRate": 0.92, - "expiresAt": "2023-09-01T14:30:00Z", - "feesIncluded": 100, - "counterpartyInformation": { - "FULL_NAME": "Jane Receiver", - "BIRTH_DATE": "1990-01-01" - }, - "paymentInstructions": [{ - "instructionsNotes": "Include reference code in transfer memo", - "accountOrWalletInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "1234567890", - "routingNumber": "021000021", - "bankName": "Chase Bank", - "referenceCode": "REF123456" - } - }], - "status": "COMPLETED", - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005" -} +Note that when passing a non-`Path` its filename is unknown so it will not be included in the request. To manually set a filename, pass a [`MultipartField`](grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt): + +```kotlin +import com.grid.api.core.MultipartField +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse +import java.io.InputStream +import java.net.URL + +val params: BulkUploadCsvParams = BulkUploadCsvParams.builder() + .file(MultipartField.builder() + .value(URL("https://example.com//path/to/file").openStream()) + .filename("/path/to/file") + .build()) + .build() +val response: BulkUploadCsvResponse = client.customers().bulk().uploadCsv(params) ``` -#### Step 5: Receive webhook notification - -When the payment status changes (to completed or failed), your platform will receive a webhook notification at your configured webhook endpoint: - -```json -{ - "transaction": { - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "COMPLETED", - "type": "OUTGOING", - "senderUmaAddress": "$sender@uma.domain", - "receiverUmaAddress": "$recipient@external.domain", - "sentAmount": { - "amount": 10550, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 9706, - "currency": { - "code": "EUR", - "name": "Euro", - "symbol": "€", - "decimals": 2 - } - }, - "userId": "User:019542f5-b3e7-1d02-0000-000000000001", - "platformUserId": "9f84e0c2a72c4fa", - "settledAt": "2023-08-15T14:30:00Z", - "createdAt": "2023-08-15T14:25:18Z", - "description": "Payment for invoice #1234", - "exchangeRate": 0.92, - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - }, - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "OUTGOING_PAYMENT" -} +## Raw responses + +The SDK defines methods that deserialize responses into instances of Kotlin classes. However, these methods don't provide access to the response headers, status code, or the raw response body. + +To access this data, prefix any HTTP method call on a client or service with `withRawResponse()`: + +```kotlin +import com.grid.api.core.http.Headers +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams + +val params: QuoteCreateParams = QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() +val quote: HttpResponseFor = client.quotes().withRawResponse().create(params) + +val statusCode: Int = quote.statusCode() +val headers: Headers = quote.headers() ``` -### Receiving Payments (Incoming Flow) - -This guide outlines the process for platforms to receive payments using the Grid API. - -#### Process Overview - -The following sequence diagram illustrates the interaction between your platform and the Grid API when receiving payments: - -```mermaid -sequenceDiagram - participant Sender as External Sender - participant Grid as Grid API - participant Client as Your Platform - participant Bank as Banking Provider - - Note over Client, Grid: One-time setup - Client->>Grid: PATCH /config (set domain, webhook URL) - Grid-->>Client: Configuration saved - Client->>Grid: POST /users (register users with bank info) - Grid-->>Client: User registered - - Note over Sender, Grid: Payment initiated by sender - Sender->>Grid: Initiates payment to UMA address - Grid->>Client: Webhook: INCOMING_PAYMENT (PENDING) - - alt Synchronous Approval/Rejection - alt Payment approved - Client-->>Grid: HTTP 200 OK (approve payment) - Grid->>Bank: Execute payment to user's bank account - Grid->>Client: Webhook: INCOMING_PAYMENT (COMPLETED) - Client-->>Grid: HTTP 200 OK (acknowledge completion) - else Payment rejected - Client-->>Grid: HTTP 403 Forbidden with rejection reason - Grid->>Sender: Payment rejected notification - end - else Asynchronous Processing (within 5 seconds) - Client-->>Grid: HTTP 202 Accepted - Client->>Grid: /transactions/{transactionId}/approve or /reject - opt Approved - Grid->>Bank: Execute payment to user's bank account - Grid->>Client: Webhook: INCOMING_PAYMENT (COMPLETED) - Client-->>Grid: HTTP 200 OK (acknowledge completion) - else Rejected - Grid->>Sender: Payment rejected notification - end - end -``` - -The process consists of five main steps: - -1. **Platform configuration** (one-time setup) to set your UMA domain and required counterparty fields -2. **Register users** with their bank account information so they can receive payments -3. **Set up webhook endpoints** to receive notifications about incoming payments -4. **Receive and approve/reject incoming payments** via webhooks -5. **Receive completion notification** when the payment completes - -#### Step 1: Platform configuration (one-time setup) - -Configure your platform settings (if you haven't already in the onboarding process), including the required counterparty information. - -```http -PATCH /config -``` - -Request body: - -```json -{ - "umaDomain": "mycompany.com", - "webhookEndpoint": "https://api.mycompany.com/webhooks/uma", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "requiredCounterpartyFields": [ - { - "name": "FULL_NAME", - "mandatory": true - }, - { - "name": "BIRTH_DATE", - "mandatory": true - } - ] - } - ] -} +You can still deserialize the response into an instance of a Kotlin class if needed: + +```kotlin +import com.grid.api.models.quotes.Quote + +val parsedQuote: Quote = quote.parse() ``` -#### Step 2: Register users with bank account information - -First, register your users in the system so they can receive payments via UMA. - -```http -POST /users -``` - -Request body: - -```json -{ - "umaAddress": "$john.sender@uma.domain.com", - "platformUserId": "9f84e0c2a72c4fa", - "userType": "INDIVIDUAL", - "fullName": "John Sender", - "birthDate": "1985-06-15", - "address": { - "line1": "123 Pine Street", - "line2": "Unit 501", - "city": "Mexico City", - "state": "Mexico City", - "postalCode": "01000", - "country": "MX" - }, - "bankAccountInfo": { - "accountType": "CLABE", - "accountNumber": "123456789012345678", - "bankName": "Banco de México" - } -} +## Error handling + +The SDK throws custom unchecked exception types: + +- [`GridServiceException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code: + + | Status | Exception | + | ------ | ------------------------------------------------------------------------------------------------------------------------ | + | 400 | [`BadRequestException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/BadRequestException.kt) | + | 401 | [`UnauthorizedException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnauthorizedException.kt) | + | 403 | [`PermissionDeniedException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/PermissionDeniedException.kt) | + | 404 | [`NotFoundException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/NotFoundException.kt) | + | 422 | [`UnprocessableEntityException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnprocessableEntityException.kt) | + | 429 | [`RateLimitException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/RateLimitException.kt) | + | 5xx | [`InternalServerException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/InternalServerException.kt) | + | others | [`UnexpectedStatusCodeException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnexpectedStatusCodeException.kt) | + +- [`GridIoException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridIoException.kt): I/O networking errors. + +- [`GridRetryableException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridRetryableException.kt): Generic error indicating a failure that could be retried by the client. + +- [`GridInvalidDataException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridInvalidDataException.kt): Failure to interpret successfully parsed data. For example, when accessing a property that's supposed to be required, but the API unexpectedly omitted it from the response. + +- [`GridException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridException.kt): Base class for all exceptions. Most errors will result in one of the previously mentioned ones, but completely generic errors may be thrown using the base class. + +## Pagination + +The SDK defines methods that return a paginated lists of results. It provides convenient ways to access the results either one page at a time or item-by-item across all pages. + +### Auto-pagination + +To iterate through all results across all pages, use the `autoPager()` method, which automatically fetches more pages as needed. + +When using the synchronous client, the method returns a [`Sequence`](https://kotlinlang.org/docs/sequences.html) + +```kotlin +import com.grid.api.models.customers.CustomerListPage + +val page: CustomerListPage = client.customers().list() +page.autoPager() + .take(50) + .forEach { customer -> println(customer) } ``` -#### Step 3: Webhook setup (one-time setup) - -Configure your webhook endpoints to receive notifications about incoming payments. You'll need to implement the webhook endpoints on your server. Remember to validate webhook signatures to ensure they are authentic. - -#### Step 4: Receive and approve incoming payments - -When someone initiates a payment to one of your users' UMA addresses, you'll receive a webhook call with a pending transaction: - -```json -{ - "transaction": { - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "PENDING", - "type": "INCOMING", - "senderUmaAddress": "$sender@external.domain", - "receiverUmaAddress": "$recipient@uma.domain", - "receivedAmount": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "userId": "User:019542f5-b3e7-1d02-0000-000000000001", - "platformUserId": "9f84e0c2a72c4fa", - "counterpartyInformation": { - "FULL_NAME": "John Sender", - "BIRTH_DATE": "1985-06-15" - }, - "reconciliationInstructions": { - "reference": "REF-123456789" - } - }, - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "INCOMING_PAYMENT" -} +When using the asynchronous client, the method returns a [`Flow`](https://kotlinlang.org/docs/flow.html): + +```kotlin +import com.grid.api.models.customers.CustomerListPageAsync + +val page: CustomerListPageAsync = client.async().customers().list() +page.autoPager() + .take(50) + .forEach { customer -> println(customer) } ``` -To approve the payment synchronously, respond with a 200 OK status. +### Manual pagination + +To access individual page items and manually request the next page, use the `items()`, +`hasNextPage()`, and `nextPage()` methods: + +```kotlin +import com.grid.api.models.customers.CustomerListPage +import com.grid.api.models.customers.CustomerOneOf + +val page: CustomerListPage = client.customers().list() +while (true) { + for (customer in page.items()) { + println(customer) + } -To reject the payment synchronously, respond with a 403 Forbidden status and a JSON body with the following fields (see API spec for error codes): + if (!page.hasNextPage()) { + break + } -```json -{ - "code": "REJECTED_BY_PLATFORM", - "message": "Payment rejected due to compliance policy.", - "reason": "FAILED_COUNTERPARTY_CHECK" + page = page.nextPage() } ``` -Alternatively, to process the payment asynchronously, return a 202 Accepted response. Then, you must call the `/transactions/{transactionId}/approve` or `/transactions/{transactionId}/reject` endpoint within 5 seconds. Synchronous approval/rejection is preferred where possible. - -#### Step 5: Receive completion notification - -When the payment completes, your webhook endpoint will receive another notification: - -```json -{ - "transaction": { - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "COMPLETED", - "type": "INCOMING", - "senderUmaAddress": "$sender@external.domain", - "receiverUmaAddress": "$recipient@uma.domain", - "receivedAmount": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "userId": "User:019542f5-b3e7-1d02-0000-000000000001", - "platformUserId": "9f84e0c2a72c4fa", - "settledAt": "2023-08-15T14:30:00Z", - "createdAt": "2023-08-15T14:25:18Z", - "description": "Payment for services", - "reconciliationInstructions": { - "reference": "REF-123456789" - }, - }, - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "INCOMING_PAYMENT" -} +## Logging + +The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor). + +Enable logging by setting the `GRID_LOG` environment variable to `info`: + +```sh +export GRID_LOG=info ``` -## User Types +Or to `debug` for more verbose logging: + +```sh +export GRID_LOG=debug +``` -The API supports both individual and business users, with different required information for each: +## ProGuard and R8 -### Individual Users +Although the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `grid-kotlin-core` is published with a [configuration file](grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage). -Required information: +ProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary. -- UMA address -- Platform user ID -- Full name -- Date of birth -- Physical address -- Bank account information +## Jackson -### Business Users +The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default. -Required information: +The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config). -- UMA address -- Platform user ID -- Business information (legal name required) -- Physical address -- Bank account information +If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`GridOkHttpClient`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt) or [`GridOkHttpClientAsync`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt). -Additional optional business information: +> [!CAUTION] +> We make no guarantee that the SDK works correctly when the Jackson version check is disabled. -- Registration number -- Tax ID +Also note that there are bugs in older Jackson versions that can affect the SDK. We don't work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead. -When creating or updating users, the `userType` field must be specified as either `INDIVIDUAL` or `BUSINESS`, and the appropriate properties for that user type must be provided. +## Network options -## Bank Account Information +### Retries -The API supports various bank account formats based on country: +The SDK automatically retries 2 times by default, with a short exponential backoff between requests. -- Mexico: CLABE -- United States: Account and routing number -- Brazil: PIX address -- International: IBAN +Only the following error types are retried: -## Webhook Verification +- Connection errors (for example, due to a network connectivity problem) +- 408 Request Timeout +- 409 Conflict +- 429 Rate Limit +- 5xx Internal -All webhooks sent by the Grid API include a signature in the `X-Grid-Signature` header, which allows you to verify the authenticity of the webhook. This is critical for security, as it ensures that only legitimate webhooks from Grid are processed by your system. +The API may also explicitly instruct the SDK to retry or not retry a request. -### Signature Verification Process +To set a custom number of retries, configure the client using the `maxRetries` method: -To verify the signature: +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient -1. **Obtain the Grid Public Key**: You should receive your P-256 (secp256r1) public key in PEM format from Grid during your integration. Store it securely (e.g., as an environment variable). -2. **Get the Raw Request Body**: It is crucial to use the exact raw byte string of the incoming request body. Do not parse or modify it before hashing. -3. **Create a SHA-256 Hash of the Request Body**: Compute the SHA-256 hash of the raw request body. -4. **Extract the Signature**: The `X-Grid-Signature` header contains a JSON string, for example: `{"v": "1", "s": "BASE64_ENCODED_SIGNATURE"}`. Parse this JSON and extract the Base64-encoded signature string from the `s` field. -5. **Decode the Signature**: Base64 decode the extracted signature string to get the raw signature bytes. -6. **Verify the Signature**: Use a standard cryptographic library that supports ECDSA with the P-256 curve and SHA-256. Verify the SHA-256 hash of the request body (from step 3) against the decoded signature bytes (from step 5) using your Grid P-256 public key. +val client: GridClient = GridOkHttpClient.builder() + .fromEnv() + .maxRetries(4) + .build() +``` -If the signature verification succeeds, the webhook is authentic. If not, it should be rejected (e.g., with an HTTP 401 Unauthorized response). +### Timeouts -### Verification Examples +Requests time out after 1 minute by default. -Note: The following examples assume you have the Grid P-256 public key (PEM encoded) stored in an environment variable `GRID_PUBLIC_KEY_PEM`. +To set a custom timeout, configure the method call using the `timeout` method: -#### Node.js Example +```kotlin +import com.grid.api.models.quotes.Quote -This example uses the built-in `crypto` module available in Node.js. +val quote: Quote = client.quotes().create( + params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build() +) +``` -```javascript -const crypto = require('crypto'); -const express = require('express'); -const app = express(); +Or configure the default for all method calls at the client level: -// The Grid P-256 public key (PEM encoded), provided during integration -const gridPublicKeyPem = process.env.GRID_PUBLIC_KEY_PEM; +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient +import java.time.Duration -if (!gridPublicKeyPem) { - console.error('Grid public key (PEM) is not configured. Please set GRID_PUBLIC_KEY_PEM environment variable.'); - process.exit(1); -} +val client: GridClient = GridOkHttpClient.builder() + .fromEnv() + .timeout(Duration.ofSeconds(30)) + .build() +``` -// Middleware to get the raw body -app.use(express.json({ - verify: (req, res, buf, encoding) => { - if (buf && buf.length) { - // Store the raw buffer for hashing, and string for potential logging (careful with PII) - req.rawBodyBuffer = buf; - req.rawBodyString = buf.toString(encoding || 'utf-8'); - } else { - req.rawBodyBuffer = Buffer.alloc(0); - req.rawBodyString = ''; - } - } -})); - -app.post('/webhooks/grid', (req, res) => { - const signatureHeader = req.header('X-Grid-Signature'); - - if (!signatureHeader) { - console.warn('Signature missing from X-Grid-Signature header'); - return res.status(401).json({ error: 'Signature missing' }); - } - - try { - let signatureBase64; - try { - const signatureObject = JSON.parse(signatureHeader); - if (typeof signatureObject.s !== 'string') { - throw new Error('Signature string (s) not found in JSON header'); - } - signatureBase64 = signatureObject.s; - } catch (jsonError) { - // Fallback for plain base64 signature if JSON parsing fails or if it's not an object with 's' - // This fallback might be removed if the JSON structure is strictly enforced. - console.warn('Failed to parse signature header as JSON, trying as plain base64: ', jsonError.message); - if (typeof signatureHeader === 'string' && signatureHeader.length > 0) { - signatureBase64 = signatureHeader; - } else { - return res.status(400).json({ error: 'Invalid signature header format' }); - } - } +### Proxies - const publicKey = crypto.createPublicKey({ - key: gridPublicKeyPem, - format: 'pem' - }); +To route requests through a proxy, configure the client using the `proxy` method: - const signatureBytes = Buffer.from(signatureBase64, 'base64'); - - // Create a SHA-256 hash of the raw request body - const requestBodyHash = crypto.createHash('sha256').update(req.rawBodyBuffer).digest(); +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient +import java.net.InetSocketAddress +import java.net.Proxy - const verify = crypto.createVerify('SHA256'); - verify.update(requestBodyHash); // Verify against the hash of the body - // OR, some libraries might expect the original data for certain verify setups: - // verify.update(req.rawBodyBuffer); // If the public key type implies the hashing algorithm. - // For clarity and safety, verifying against the explicit hash is better. +val client: GridClient = GridOkHttpClient.builder() + .fromEnv() + .proxy(Proxy( + Proxy.Type.HTTP, InetSocketAddress( + "https://example.com", 8080 + ) + )) + .build() +``` - const isValid = verify.verify(publicKey, signatureBytes); +### HTTPS - if (!isValid) { - console.warn('Invalid signature'); - return res.status(401).json({ error: 'Invalid signature' }); - } +> [!NOTE] +> Most applications should not call these methods, and instead use the system defaults. The defaults include +> special optimizations that can be lost if the implementations are modified. - // Webhook is verified, process it based on type - const webhookData = req.body; // req.body is the parsed JSON from express.json() - console.log(`Webhook verified and processing type: ${webhookData.type}`); - - // Process webhookData.type... - // Example: if (webhookData.type === 'INCOMING_PAYMENT') { /* ... */ } - - return res.status(200).json({ received: true }); - - } catch (error) { - console.error('Error during signature verification:', error.message, error.stack); - return res.status(500).json({ error: 'Signature verification process error' }); - } -}); - -const port = process.env.PORT || 3000; -app.listen(port, () => { - console.log(`Webhook server listening on port ${port}`); -}); -``` - -#### Python Example - -This example uses the `ecdsa` library for P-256 signature verification and `hashlib` for SHA256 hashing. You may need to install it: `pip install ecdsa hashlib`. - -```python -import os -import base64 -import json -import hashlib -from ecdsa import VerifyingKey, NIST256p, BadSignatureError -from flask import Flask, request, jsonify - -app = Flask(__name__) - -# The Grid P-256 public key (PEM encoded), provided during integration -GRID_PUBLIC_KEY_PEM = os.environ.get('GRID_PUBLIC_KEY_PEM') - -if not GRID_PUBLIC_KEY_PEM: - raise ValueError("Grid public key (PEM) is not configured. Please set GRID_PUBLIC_KEY_PEM environment variable.") - -try: - # The hashfunc=hashlib.sha256 here is for the key itself if needed, not for the data hash verification method directly. - # The verify method will take the data hash. - GRID_VERIFY_KEY = VerifyingKey.from_pem(GRID_PUBLIC_KEY_PEM, hashfunc=hashlib.sha256) -except Exception as e: - raise ValueError(f"Failed to load Grid public key from PEM: {e}") +To configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods: -@app.route('/webhooks/grid', methods=['POST']) -def handle_webhook(): - signature_header = request.headers.get('X-Grid-Signature') - if not signature_header: - print("Signature missing from X-Grid-Signature header") - return jsonify({'error': 'Signature missing'}), 401 +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient - try: - signature_base64 = json.loads(signature_header).get('s') - if not isinstance(signature_base64, str): - raise ValueError("Signature string (s) not found or not a string in JSON header") - except (json.JSONDecodeError, ValueError) as e: - # Fallback for plain base64 signature for robustness, though spec implies JSON. - print(f"Could not parse signature header as JSON or 's' field missing/invalid: {e}. Trying as plain base64.") - if isinstance(signature_header, str) and len(signature_header) > 0: - signature_base64 = signature_header - else: - return jsonify({'error': 'Invalid signature header format'}), 400 +val client: GridClient = GridOkHttpClient.builder() + .fromEnv() + // If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa. + .sslSocketFactory(yourSSLSocketFactory) + .trustManager(yourTrustManager) + .hostnameVerifier(yourHostnameVerifier) + .build() +``` - try: - signature_bytes = base64.b64decode(signature_base64) - except Exception as e: - print(f"Invalid base64 encoding for signature: {e}") - return jsonify({'error': 'Invalid signature encoding'}), 401 +### Custom HTTP client - # Get raw request body and hash it with SHA-256 - request_body_bytes = request.get_data() - request_body_hash_bytes = hashlib.sha256(request_body_bytes).digest() +The SDK consists of three artifacts: - try: - # The verify method takes the signature and the hash of the data. - # The curve (NIST256p) is inherent in the VerifyingKey object. - # The hashfunc parameter in verify is for the digest algorithm if the key can be used with multiple, - # but for ECDSA it's usually tied to the key or specified at key loading. - # Here, we pass the raw hash. - is_valid = GRID_VERIFY_KEY.verify(signature_bytes, request_body_hash_bytes, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der) - # Note: sigdecode=ecdsa.util.sigdecode_der might be needed if the signature is in DER format. - # If the signature is just raw r and s values concatenated, sigdecode might not be needed or a different one used. - # Assuming DER which is common for ECDSA signatures. - except BadSignatureError: - print("Invalid signature") - return jsonify({'error': 'Invalid signature'}), 401 - except Exception as e: - print(f"Error during signature verification: {e}") - return jsonify({'error': 'Signature verification process error'}), 500 +- `grid-kotlin-core` + - Contains core SDK logic + - Does not depend on [OkHttp](https://square.github.io/okhttp) + - Exposes [`GridClient`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClient.kt), [`GridClientAsync`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsync.kt), [`GridClientImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt), and [`GridClientAsyncImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt), all of which can work with any HTTP client +- `grid-kotlin-client-okhttp` + - Depends on [OkHttp](https://square.github.io/okhttp) + - Exposes [`GridOkHttpClient`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt) and [`GridOkHttpClientAsync`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt), which provide a way to construct [`GridClientImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt) and [`GridClientAsyncImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt), respectively, using OkHttp +- `grid-kotlin` + - Depends on and exposes the APIs of both `grid-kotlin-core` and `grid-kotlin-client-okhttp` + - Does not have its own logic - # Webhook is verified, process it based on type - webhook_data = request.json # This is the parsed JSON from Flask - print(f"Webhook verified and processing type: {webhook_data.get('type')}") - - # Process webhook_data.get('type') - # Example: if webhook_data.get('type') == 'INCOMING_PAYMENT': - # print("Processing INCOMING_PAYMENT webhook...") +This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies. - return jsonify({'received': True}), 200 +#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html) -if __name__ == '__main__': - port = int(os.environ.get("PORT", 3000)) - app.run(host='0.0.0.0', port=port) -``` +> [!TIP] +> Try the available [network options](#network-options) before replacing the default client. -### Security Considerations +To use a customized `OkHttpClient`: -- **Always verify signatures**: Never process webhooks without verifying their signatures. -- **Use HTTPS**: Ensure your webhook endpoint uses HTTPS to prevent man-in-the-middle attacks. -- **Implement idempotency**: Use the `webhookId` field to prevent processing duplicate webhooks. -- **Timeout handling**: Implement proper timeout handling and respond to webhooks promptly. +1. Replace your [`grid-kotlin` dependency](#installation) with `grid-kotlin-core` +2. Copy `grid-kotlin-client-okhttp`'s [`OkHttpClient`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/OkHttpClient.kt) class into your code and customize it +3. Construct [`GridClientImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt) or [`GridClientAsyncImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt), similarly to [`GridOkHttpClient`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt) or [`GridOkHttpClientAsync`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt), using your customized client -## Development +### Completely custom HTTP client -### Requirements +To use a completely custom HTTP client: -- Node.js v16 or later -- npm v6 or later +1. Replace your [`grid-kotlin` dependency](#installation) with `grid-kotlin-core` +2. Write a class that implements the [`HttpClient`](grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpClient.kt) interface +3. Construct [`GridClientImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt) or [`GridClientAsyncImpl`](grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt), similarly to [`GridOkHttpClient`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt) or [`GridOkHttpClientAsync`](grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt), using your new client class -### Setup +## Undocumented API functionality -1. Clone this repository -2. Install dependencies: `npm install` -3. Build documentation: `npm run build` +The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API. -### Building Documentation +### Parameters -To generate the documentation, you'll need Node.js (v16 or later) installed. +To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class: -```bash -# Install dependencies -npm install +```kotlin +import com.grid.api.core.JsonValue +import com.grid.api.models.quotes.QuoteCreateParams -# Or use make and build all -make install -make build +val params: QuoteCreateParams = QuoteCreateParams.builder() + .putAdditionalHeader("Secret-Header", "42") + .putAdditionalQueryParam("secret_query_param", "42") + .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) + .build() ``` -This will bundle and mirror the full openapi spec into the `openapi.yaml` file and the `mintlify/openapi.yaml` file. +These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods. -### Serving Documentation Locally +To set undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class: -You can serve the documentation locally for development purposes: +```kotlin +import com.grid.api.core.JsonValue +import com.grid.api.models.transferin.TransferInCreateParams -```bash -# Serve Mintlify documentation: +val params: TransferInCreateParams = TransferInCreateParams.builder() + .destination(TransferInCreateParams.Destination.builder() + .putAdditionalProperty("secretProperty", JsonValue.from("42")) + .build()) + .build() +``` -# First install mint if you haven't already -npm i -g mint +These properties can be accessed on the nested built object later using the `_additionalProperties()` method. -mint dev # or make mint +To set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt) object to its setter: + +```kotlin +import com.grid.api.core.JsonValue +import com.grid.api.models.quotes.QuoteCreateParams + +val params: QuoteCreateParams = QuoteCreateParams.builder() + .destination(JsonValue.from(42)) + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() ``` -## OpenAPI +The most straightforward way to create a [`JsonValue`](grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt) is using its `from(...)` method: + +```kotlin +import com.grid.api.core.JsonValue + +// Create primitive JSON values +val nullValue: JsonValue = JsonValue.from(null) +val booleanValue: JsonValue = JsonValue.from(true) +val numberValue: JsonValue = JsonValue.from(42) +val stringValue: JsonValue = JsonValue.from("Hello World!") + +// Create a JSON array value equivalent to `["Hello", "World"]` +val arrayValue: JsonValue = JsonValue.from(listOf( + "Hello", "World" +)) + +// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }` +val objectValue: JsonValue = JsonValue.from(mapOf( + "a" to 1, "b" to 2 +)) + +// Create an arbitrarily nested JSON equivalent to: +// { +// "a": [1, 2], +// "b": [3, 4] +// } +val complexValue: JsonValue = JsonValue.from(mapOf( + "a" to listOf( + 1, 2 + ), "b" to listOf( + 3, 4 + ) +)) +``` -We use Redocly to split and merge OpenAPI schema you can install it with: +Normally a `Builder` class's `build` method will throw [`IllegalStateException`](https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalStateException.html) if any required parameter or property is unset. -`npm i -g @redocly/cli@latest` +To forcibly omit a required parameter or property, pass [`JsonMissing`](grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt): -### Merging openapi schema +```kotlin +import com.grid.api.core.JsonMissing +import com.grid.api.models.quotes.QuoteCreateParams -You can merge openapi schema with -`redocly bundle openapi/openapi.yaml -o openapi.yaml` +val params: QuoteCreateParams = QuoteCreateParams.builder() + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .destination(JsonMissing.of()) + .build() +``` + +### Response properties -### Linting the OpenAPI Schema +To access undocumented response properties, call the `_additionalProperties()` method: -We use Redocly to lint the OpenAPI schema and markdown-lint to lint the markdown documentation: +```kotlin +import com.grid.api.core.JsonBoolean +import com.grid.api.core.JsonNull +import com.grid.api.core.JsonNumber +import com.grid.api.core.JsonValue -```bash -npm run lint -# Or: make lint +val additionalProperties: Map = client.quotes().create(params)._additionalProperties() +val secretPropertyValue: JsonValue = additionalProperties.get("secretProperty") + +val result = when (secretPropertyValue) { + is JsonNull -> "It's null!" + is JsonBoolean -> "It's a boolean!" + is JsonNumber -> "It's a number!" + // Other types include `JsonMissing`, `JsonString`, `JsonArray`, and `JsonObject` + else -> "It's something else!" +} ``` -## Claude Code Skill +To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method: -This repository includes a [Claude Code](https://claude.ai/code) skill for interacting with the Grid API. The skill enables Claude to execute API operations, answer documentation questions, and guide you through payment workflows. +```kotlin +import com.grid.api.core.JsonField +import com.grid.api.models.quotes.QuoteDestinationOneOf -### Setup +val destination: JsonField = client.quotes().create(params)._destination() -Before using the skill, build the CLI tool: -```bash -cd cli && npm install && npm run build && cd .. +if (destination.isMissing()) { + // The property is absent from the JSON response +} else if (destination.isNull()) { + // The property was set to literal null +} else { + // Check if value was provided as a string + // Other methods include `asNumber()`, `asBoolean()`, etc. + val jsonString: String? = destination.asString(); + + // Try to deserialize into a custom type + val myObject: MyClass = destination.asUnknown()!!.convert(MyClass::class.java) +} ``` -Then configure credentials (see Configuration section below). +### Response validation + +In rare cases, the API may return a response that doesn't match the expected type. For example, the SDK may expect a property to contain a `String`, but the API could return something else. -### Usage +By default, the SDK will not throw an exception in this case. It will throw [`GridInvalidDataException`](grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridInvalidDataException.kt) only if you directly access the property. -When using Claude Code in this repository, invoke the skill with `/grid-api` or ask questions like: +If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`: -- "Send a payment to Mexico" -- "Create a quote from USDC to INR" -- "What currencies does Grid support?" -- "Help me set up a UPI account" +```kotlin +import com.grid.api.models.quotes.Quote -The skill uses the CLI at `cli/dist/index.js` to execute operations against the Grid API. +val quote: Quote = client.quotes().create(params).validate() +``` -### Configuration +Or configure the method call to validate the response using the `responseValidation` method: -Set your API credentials as environment variables: +```kotlin +import com.grid.api.models.quotes.Quote -```bash -export GRID_API_TOKEN_ID="your-token-id" -export GRID_API_CLIENT_SECRET="your-client-secret" +val quote: Quote = client.quotes().create( + params, RequestOptions.builder().responseValidation(true).build() +) ``` -## Support +Or configure the default for all method calls at the client level: + +```kotlin +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient + +val client: GridClient = GridOkHttpClient.builder() + .fromEnv() + .responseValidation(true) + .build() +``` + +## FAQ + +### Why don't you use plain `enum` classes? + +Kotlin `enum` classes are not trivially [forwards compatible](https://www.stainless.com/blog/making-java-enums-forwards-compatible). Using them in the SDK could cause runtime exceptions if the API is updated to respond with a new enum value. + +### Why do you represent fields using `JsonField` instead of just plain `T`? + +Using `JsonField` enables a few features: + +- Allowing usage of [undocumented API functionality](#undocumented-api-functionality) +- Lazily [validating the API response against the expected shape](#response-validation) +- Representing absent vs explicitly null values + +### Why don't you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)? + +It is not [backwards compatible to add new fields to a data class](https://kotlinlang.org/docs/api-guidelines-backward-compatibility.html#avoid-using-data-classes-in-your-api) and we don't want to introduce a breaking change every time we add a field to a class. + +### Why don't you use checked exceptions? + +Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason. + +Checked exceptions: + +- Are verbose to handle +- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error +- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function) +- Don't play well with lambdas (also due to the function coloring problem) + +## Semantic versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +2. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -For any questions or issues, please contact Grid support at . +We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/grid-kotlin/issues) with questions, bugs, or suggestions. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..1937e0f7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Grid, please follow the respective company's security reporting guidelines. + +### Grid Terms and Policies + +Please contact support@lightspark.com for any questions or concerns regarding the security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..43a2719a --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("io.github.gradle-nexus.publish-plugin") version "1.1.0" + id("org.jetbrains.dokka") version "2.0.0" +} + +repositories { + mavenCentral() +} + +allprojects { + group = "com.grid.api" + version = "0.0.1" +} + +subprojects { + // These are populated with dependencies by `buildSrc` scripts. + tasks.register("format") { + group = "Verification" + description = "Formats all source files." + } + tasks.register("lint") { + group = "Verification" + description = "Verifies all source files are formatted." + } + apply(plugin = "org.jetbrains.dokka") +} + +subprojects { + apply(plugin = "org.jetbrains.dokka") +} + +// Avoid race conditions between `dokkaHtmlCollector` and `dokkaJavadocJar` tasks +tasks.named("dokkaHtmlCollector").configure { + subprojects.flatMap { it.tasks } + .filter { it.project.name != "grid-kotlin" && it.name == "dokkaJavadocJar" } + .forEach { mustRunAfter(it) } +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + + username.set(System.getenv("SONATYPE_USERNAME")) + password.set(System.getenv("SONATYPE_PASSWORD")) + } + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..0b141353 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + `kotlin-dsl` + kotlin("jvm") version "1.9.20" +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20") +} diff --git a/buildSrc/src/main/kotlin/grid.java.gradle.kts b/buildSrc/src/main/kotlin/grid.java.gradle.kts new file mode 100644 index 00000000..81d5d32b --- /dev/null +++ b/buildSrc/src/main/kotlin/grid.java.gradle.kts @@ -0,0 +1,136 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat + +plugins { + `java-library` +} + +repositories { + mavenCentral() +} + +configure { + withJavadocJar() + withSourcesJar() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } + + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().configureEach { + options.compilerArgs.add("-Werror") + options.release.set(8) +} + +tasks.named("javadocJar") { + setZip64(true) +} + +tasks.named("jar") { + manifest { + attributes(mapOf( + "Implementation-Title" to project.name, + "Implementation-Version" to project.version + )) + } +} + +tasks.withType().configureEach { + useJUnitPlatform() + + // Run tests in parallel to some degree. + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) + forkEvery = 100 + + testLogging { + exceptionFormat = TestExceptionFormat.FULL + } +} + +val palantir by configurations.creating +dependencies { + palantir("com.palantir.javaformat:palantir-java-format:2.73.0") +} + +fun registerPalantir( + name: String, + description: String, +) { + val javaName = "${name}Java" + tasks.register(javaName) { + group = "Verification" + this.description = description + + classpath = palantir + mainClass = "com.palantir.javaformat.java.Main" + + // Avoid an `IllegalAccessError` on Java 9+. + jvmArgs( + "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + ) + + // Use paths relative to the current module. + val argumentFile = + project.layout.buildDirectory.file("palantir-$name-args.txt").get().asFile + val lastRunTimeFile = + project.layout.buildDirectory.file("palantir-$name-last-run.txt").get().asFile + + // Read the time when this task was last executed for this module (if ever). + val lastRunTime = lastRunTimeFile.takeIf { it.exists() }?.readText()?.toLongOrNull() ?: 0L + + // Use a `fileTree` relative to the module's source directory. + val javaFiles = project.fileTree("src") { include("**/*.java") } + + // Determine if any files need to be formatted or linted and continue only if there is at least + // one file. + onlyIf { javaFiles.any { it.lastModified() > lastRunTime } } + + inputs.files(javaFiles) + + doFirst { + // Create the argument file and set the preferred formatting style. + argumentFile.parentFile.mkdirs() + argumentFile.writeText("--palantir\n") + + if (name == "lint") { + // For lint, do a dry run, so no files are modified. Set the exit code to 1 (instead of + // the default 0) if any files need to be formatted, indicating that linting has failed. + argumentFile.appendText("--dry-run\n") + argumentFile.appendText("--set-exit-if-changed\n") + } else { + // `--dry-run` and `--replace` (for in-place formatting) are mutually exclusive. + argumentFile.appendText("--replace\n") + } + + // Write the modified files to the argument file. + javaFiles.filter { it.lastModified() > lastRunTime } + .forEach { argumentFile.appendText("${it.absolutePath}\n") } + } + + doLast { + // Record the last execution time for later up-to-date checking. + lastRunTimeFile.writeText(System.currentTimeMillis().toString()) + } + + // Pass the argument file using the @ symbol + args = listOf("@${argumentFile.absolutePath}") + + outputs.upToDateWhen { javaFiles.none { it.lastModified() > lastRunTime } } + } + + tasks.named(name) { + dependsOn(tasks.named(javaName)) + } +} + +registerPalantir(name = "format", description = "Formats all Java source files.") +registerPalantir(name = "lint", description = "Verifies all Java source files are formatted.") diff --git a/buildSrc/src/main/kotlin/grid.kotlin.gradle.kts b/buildSrc/src/main/kotlin/grid.kotlin.gradle.kts new file mode 100644 index 00000000..a9c25684 --- /dev/null +++ b/buildSrc/src/main/kotlin/grid.kotlin.gradle.kts @@ -0,0 +1,109 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + +plugins { + id("grid.java") + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } + + compilerOptions { + freeCompilerArgs = listOf( + "-Xjvm-default=all", + "-Xjdk-release=1.8", + // Suppress deprecation warnings because we may still reference and test deprecated members. + // TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+. + "-nowarn", + ) + jvmTarget.set(JvmTarget.JVM_1_8) + languageVersion.set(KotlinVersion.KOTLIN_1_8) + apiVersion.set(KotlinVersion.KOTLIN_1_8) + coreLibrariesVersion = "1.8.0" + } +} + +tasks.withType().configureEach { + systemProperty("junit.jupiter.execution.parallel.enabled", true) + systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") + + // `SKIP_MOCK_TESTS` affects which tests run so it must be added as input for proper cache invalidation. + inputs.property("skipMockTests", System.getenv("SKIP_MOCK_TESTS")).optional(true) +} + +val ktfmt by configurations.creating +dependencies { + ktfmt("com.facebook:ktfmt:0.56") +} + +fun registerKtfmt( + name: String, + description: String, +) { + val kotlinName = "${name}Kotlin" + tasks.register(kotlinName) { + group = "Verification" + this.description = description + + classpath = ktfmt + mainClass = "com.facebook.ktfmt.cli.Main" + + // Use paths relative to the current module. + val argumentFile = project.layout.buildDirectory.file("ktfmt-$name-args.txt").get().asFile + val lastRunTimeFile = + project.layout.buildDirectory.file("ktfmt-$name-last-run.txt").get().asFile + + // Read the time when this task was last executed for this module (if ever). + val lastRunTime = lastRunTimeFile.takeIf { it.exists() }?.readText()?.toLongOrNull() ?: 0L + + // Use a `fileTree` relative to the module's source directory. + val kotlinFiles = project.fileTree("src") { include("**/*.kt") } + + // Determine if any files need to be formatted or linted and continue only if there is at least + // one file (otherwise Ktfmt will fail). + onlyIf { kotlinFiles.any { it.lastModified() > lastRunTime } } + + inputs.files(kotlinFiles) + + doFirst { + // Create the argument file and set the preferred formatting style. + argumentFile.parentFile.mkdirs() + argumentFile.writeText("--kotlinlang-style\n") + + if (name == "lint") { + // For lint, do a dry run, so no files are modified. Set the exit code to 1 (instead of + // the default 0) if any files need to be formatted, indicating that linting has failed. + argumentFile.appendText("--dry-run\n") + argumentFile.appendText("--set-exit-if-changed\n") + } + + // Write the modified files to the argument file. + kotlinFiles.filter { it.lastModified() > lastRunTime } + .forEach { argumentFile.appendText("${it.absolutePath}\n") } + } + + doLast { + // Record the last execution time for later up-to-date checking. + lastRunTimeFile.writeText(System.currentTimeMillis().toString()) + } + + // Pass the argument file using the @ symbol + args = listOf("@${argumentFile.absolutePath}") + + outputs.upToDateWhen { kotlinFiles.none { it.lastModified() > lastRunTime } } + } + + tasks.named(name) { + dependsOn(tasks.named(kotlinName)) + } +} + +registerKtfmt(name = "format", description = "Formats all Kotlin source files.") +registerKtfmt(name = "lint", description = "Verifies all Kotlin source files are formatted.") diff --git a/buildSrc/src/main/kotlin/grid.publish.gradle.kts b/buildSrc/src/main/kotlin/grid.publish.gradle.kts new file mode 100644 index 00000000..de3ddad4 --- /dev/null +++ b/buildSrc/src/main/kotlin/grid.publish.gradle.kts @@ -0,0 +1,69 @@ +plugins { + `maven-publish` + signing +} + +configure { + publications { + register("maven") { + from(components["java"]) + + pom { + name.set("Grid API") + description.set("API for managing global payments on the open Money Grid. Built by Lightspark.\nSee the full documentation at https://grid.lightspark.com/.") + url.set("grid.lightspark.com") + + licenses { + license { + name.set("Apache-2.0") + } + } + + developers { + developer { + name.set("Grid") + email.set("support@lightspark.com") + } + } + + scm { + connection.set("scm:git:git://github.com/stainless-sdks/grid-kotlin.git") + developerConnection.set("scm:git:git://github.com/stainless-sdks/grid-kotlin.git") + url.set("https://github.com/stainless-sdks/grid-kotlin") + } + + versionMapping { + allVariants { + fromResolutionResult() + } + } + } + } + } + repositories { + if (project.hasProperty("publishLocal")) { + maven { + name = "LocalFileSystem" + url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo") + } + } + } +} + +signing { + val signingKeyId = System.getenv("GPG_SIGNING_KEY_ID")?.ifBlank { null } + val signingKey = System.getenv("GPG_SIGNING_KEY")?.ifBlank { null } + val signingPassword = System.getenv("GPG_SIGNING_PASSWORD")?.ifBlank { null } + if (signingKey != null && signingPassword != null) { + useInMemoryPgpKeys( + signingKeyId, + signingKey, + signingPassword, + ) + sign(publishing.publications["maven"]) + } +} + +tasks.named("publish") { + dependsOn(":closeAndReleaseSonatypeStagingRepository") +} diff --git a/cli/.gitignore b/cli/.gitignore deleted file mode 100644 index be4d2b83..00000000 --- a/cli/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Build output -dist/ - -# Dependencies -node_modules/ - -# Environment -.env -.env.local diff --git a/cli/README.md b/cli/README.md deleted file mode 100644 index 3f126f37..00000000 --- a/cli/README.md +++ /dev/null @@ -1,416 +0,0 @@ -# Grid CLI - -A command-line interface for the Grid API, enabling global payments across fiat, stablecoins, and Bitcoin. - -## Installation - -```bash -cd cli -npm install -npm run build -``` - -## Quick Start - -Configure your credentials interactively: - -```bash -node cli/dist/index.js configure -``` - -This will prompt for your API Token ID and Client Secret, validate them, and save to `~/.grid-credentials`. - -Alternatively, set environment variables: - -```bash -export GRID_API_TOKEN_ID="your-token-id" -export GRID_API_CLIENT_SECRET="your-client-secret" -``` - -## Usage - -Run commands from the repository root: - -```bash -node cli/dist/index.js [options] -``` - -### Global Options - -| Option | Description | -|--------|-------------| -| `-c, --config ` | Path to credentials file | -| `-u, --base-url ` | Override API base URL | -| `-f, --format ` | Output format: `json` (default) or `table` | -| `--no-color` | Disable colored output | -| `-V, --version` | Show version | -| `-h, --help` | Show help | - -### Command Aliases - -For convenience, common commands have short aliases: - -| Alias | Command | -|-------|---------| -| `cust` | `customers` | -| `tx` | `transactions` | -| `acct` | `accounts` | - -Example: `grid tx list` is equivalent to `grid transactions list` - -## Commands - -### Setup - -```bash -# Interactive configuration -node cli/dist/index.js configure - -# Non-interactive configuration -node cli/dist/index.js configure --token-id --client-secret - -# Skip credential verification -node cli/dist/index.js configure --no-verify -``` - -### Platform Configuration - -```bash -# Get platform config (currencies, limits, webhook) -node cli/dist/index.js config get - -# Update webhook endpoint -node cli/dist/index.js config update --webhook-endpoint https://example.com/webhooks -``` - -### Customers - -```bash -# List customers -node cli/dist/index.js customers list [--limit 20] [--type INDIVIDUAL|BUSINESS] - -# Get customer details -node cli/dist/index.js customers get - -# Create individual customer -node cli/dist/index.js customers create \ - --platform-id "your-id" \ - --type INDIVIDUAL \ - --full-name "John Doe" \ - --birth-date "1990-01-15" \ - --address-line1 "123 Main St" \ - --address-city "Seattle" \ - --address-state "WA" \ - --address-postal "98101" \ - --address-country "US" - -# Create business customer -node cli/dist/index.js customers create \ - --platform-id "biz-123" \ - --type BUSINESS \ - --legal-name "Acme Inc" \ - --tax-id "12-3456789" - -# Generate KYC link -node cli/dist/index.js customers kyc-link \ - --customer-id \ - --redirect-url https://example.com/kyc-complete - -# Update customer -node cli/dist/index.js customers update --full-name "Jane Doe" - -# Delete customer (prompts for confirmation) -node cli/dist/index.js customers delete - -# Delete customer without confirmation -node cli/dist/index.js customers delete --yes -``` - -### Accounts - -#### Internal Accounts (Grid-managed balances) - -```bash -# List customer internal accounts -node cli/dist/index.js accounts internal list [--customer-id ] [--currency USD] - -# List platform internal accounts -node cli/dist/index.js accounts internal list --platform -``` - -#### External Accounts (Bank accounts, wallets) - -```bash -# List external accounts -node cli/dist/index.js accounts external list [--customer-id ] - -# Create US bank account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency USD \ - --account-type US_ACCOUNT \ - --account-number "123456789" \ - --routing-number "021000021" \ - --account-category CHECKING \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "John Doe" - -# Create Mexico CLABE account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency MXN \ - --account-type CLABE \ - --clabe "012345678901234567" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Carlos Garcia" \ - --beneficiary-birth-date "1988-03-20" \ - --beneficiary-nationality MX - -# Create India UPI account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency INR \ - --account-type UPI \ - --upi-id "name@okaxis" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Rajesh Kumar" \ - --beneficiary-birth-date "1985-06-15" \ - --beneficiary-nationality IN - -# Create Brazil PIX account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency BRL \ - --account-type PIX \ - --pix-key "12345678901" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Maria Silva" \ - --beneficiary-birth-date "1990-05-10" \ - --beneficiary-nationality BR - -# Create Nigeria account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency NGN \ - --account-type NGN_ACCOUNT \ - --account-number "1234567890" \ - --bank-name "First Bank" \ - --purpose GOODS_OR_SERVICES \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Chidi Okonkwo" \ - --beneficiary-birth-date "1992-08-20" \ - --beneficiary-nationality NG - -# Create Europe IBAN account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency EUR \ - --account-type IBAN \ - --iban "DE89370400440532013000" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Hans Mueller" - -# Create crypto wallet (Solana USDC) -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency USDC \ - --account-type SOLANA_WALLET \ - --address "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU" -``` - -### Quotes (Cross-Currency Transfers) - -```bash -# List quotes -node cli/dist/index.js quotes list [--status PENDING] [--customer-id ] - -# Get quote details -node cli/dist/index.js quotes get - -# Create quote from internal account (prefunded) -node cli/dist/index.js quotes create \ - --source-account \ - --dest-uma '$user@domain.com' \ - --amount 10000 \ - --lock-side SENDING - -# Create quote with JIT funding (real-time) -node cli/dist/index.js quotes create \ - --source-customer \ - --source-currency USDC \ - --dest-account \ - --dest-currency MXN \ - --amount 100000 \ - --lock-side RECEIVING - -# Execute a pending quote -node cli/dist/index.js quotes execute -``` - -### Same-Currency Transfers - -```bash -# Transfer in (external → internal) -node cli/dist/index.js transfers in \ - --source \ - --dest \ - --amount 10000 - -# Transfer out (internal → external) -node cli/dist/index.js transfers out \ - --source \ - --dest \ - --amount 10000 -``` - -### Transactions - -```bash -# List transactions -node cli/dist/index.js transactions list \ - [--customer-id ] \ - [--status PENDING|PROCESSING|COMPLETED|FAILED] \ - [--type INCOMING|OUTGOING] \ - [--start-date 2024-01-01] \ - [--end-date 2024-12-31] - -# Get transaction details -node cli/dist/index.js transactions get - -# Approve incoming payment -node cli/dist/index.js transactions approve - -# Reject incoming payment -node cli/dist/index.js transactions reject --reason "Invalid sender" -``` - -### Receiver Lookup - -```bash -# Look up UMA address -node cli/dist/index.js receiver lookup-uma '$user@domain.com' - -# Look up external account -node cli/dist/index.js receiver lookup-account -``` - -### Sandbox Testing - -```bash -# Fund an internal account -node cli/dist/index.js sandbox fund --amount 100000 - -# Simulate sending funds to a JIT quote -node cli/dist/index.js sandbox send --quote-id --currency USDC - -# Simulate receiving an UMA payment -node cli/dist/index.js sandbox receive \ - --uma-address '$user@domain.com' \ - --amount 1000 \ - --currency USD -``` - -## Output Format - -All commands output JSON: - -```json -{ - "success": true, - "data": { ... } -} -``` - -On error: - -```json -{ - "success": false, - "error": { - "code": "ERROR_CODE", - "message": "Human readable message" - } -} -``` - -## Common Workflows - -### Send USDC to Mexico (JIT Funding) - -```bash -# 1. Create external account -node cli/dist/index.js accounts external create \ - --customer-id \ - --currency MXN \ - --account-type CLABE \ - --clabe "012345678901234567" \ - --beneficiary-type INDIVIDUAL \ - --beneficiary-name "Carlos Garcia" \ - --beneficiary-birth-date "1988-03-20" \ - --beneficiary-nationality MX - -# 2. Create quote (returns payment instructions) -node cli/dist/index.js quotes create \ - --source-customer \ - --source-currency USDC \ - --dest-account \ - --dest-currency MXN \ - --amount 100000 \ - --lock-side RECEIVING - -# 3. In sandbox, simulate the USDC deposit -node cli/dist/index.js sandbox send --quote-id --currency USDC - -# 4. Check transaction status -node cli/dist/index.js transactions get -``` - -### Send to UMA Address - -```bash -# 1. Look up the receiver -node cli/dist/index.js receiver lookup-uma '$alice@example.com' - -# 2. Create and execute quote -node cli/dist/index.js quotes create \ - --source-account \ - --dest-uma '$alice@example.com' \ - --amount 5000 \ - --lock-side SENDING - -# 3. Execute the quote -node cli/dist/index.js quotes execute -``` - -## Output Formats - -### JSON (default) - -```bash -node cli/dist/index.js customers list -``` - -Output includes syntax highlighting when running in a terminal. - -### Table - -```bash -node cli/dist/index.js customers list --format table -``` - -Displays results in a human-readable table format. - -### Disable Colors - -```bash -node cli/dist/index.js customers list --no-color -``` - -## Notes - -- All amounts are in the **smallest currency unit** (cents for USD, satoshis for BTC) -- Quotes expire in 1-5 minutes -- JIT quotes auto-execute when funds are received (no manual execute needed) -- Use `--lock-side SENDING` to fix the send amount, `RECEIVING` to fix the receive amount -- Destructive operations (like delete) require confirmation unless `--yes` is passed -- Input validation runs before API calls to catch errors early diff --git a/cli/package-lock.json b/cli/package-lock.json deleted file mode 100644 index 0ec237b5..00000000 --- a/cli/package-lock.json +++ /dev/null @@ -1,251 +0,0 @@ -{ - "name": "grid-cli", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "grid-cli", - "version": "1.0.0", - "license": "Apache-2.0", - "dependencies": { - "commander": "^12.1.0" - }, - "bin": { - "grid": "dist/index.js" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.4.0" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", - "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - } - } -} diff --git a/cli/package.json b/cli/package.json deleted file mode 100644 index d56c2183..00000000 --- a/cli/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "grid-cli", - "version": "1.0.0", - "description": "CLI tool for Grid API", - "main": "dist/index.js", - "bin": { - "grid": "./dist/index.js" - }, - "engines": { - "node": ">=18.0.0" - }, - "scripts": { - "build": "tsc", - "dev": "ts-node src/index.ts", - "start": "node dist/index.js", - "clean": "rm -rf dist" - }, - "dependencies": { - "commander": "^12.1.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.4.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "author": "Lightspark", - "license": "Apache-2.0" -} diff --git a/cli/src/client.ts b/cli/src/client.ts deleted file mode 100644 index af06189e..00000000 --- a/cli/src/client.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { GridConfig } from "./config"; - -export interface ApiResponse { - success: boolean; - data?: T; - error?: { - status: number; - code?: string; - message: string; - details?: unknown; - }; -} - -export interface PaginatedResponse { - data: T[]; - hasMore: boolean; - nextCursor?: string; - totalCount?: number; -} - -export class GridClient { - private config: GridConfig; - private timeoutMs: number; - - constructor(config: GridConfig, timeoutMs: number = 30000) { - this.config = config; - this.timeoutMs = timeoutMs; - } - - private getAuthHeader(): string { - const credentials = `${this.config.apiTokenId}:${this.config.apiClientSecret}`; - return `Basic ${Buffer.from(credentials).toString("base64")}`; - } - - private buildUrl( - path: string, - params?: Record - ): string { - const baseUrl = this.config.baseUrl.endsWith("/") - ? this.config.baseUrl - : this.config.baseUrl + "/"; - const fullPath = path.startsWith("/") ? path.slice(1) : path; - const url = new URL(fullPath, baseUrl); - if (params) { - Object.entries(params).forEach(([key, value]) => { - if (value !== undefined) { - url.searchParams.append(key, String(value)); - } - }); - } - return url.toString(); - } - - async request( - method: string, - path: string, - options?: { - params?: Record; - body?: unknown; - } - ): Promise> { - const url = this.buildUrl(path, options?.params); - - const headers: Record = { - Authorization: this.getAuthHeader(), - Accept: "application/json", - }; - - const fetchOptions: RequestInit = { - method, - headers, - }; - - if (options?.body) { - headers["Content-Type"] = "application/json"; - fetchOptions.body = JSON.stringify(options.body); - } - - try { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs); - fetchOptions.signal = controller.signal; - - const response = await fetch(url, fetchOptions); - clearTimeout(timeoutId); - const contentType = response.headers.get("content-type"); - let data: unknown = null; - - if (contentType?.includes("application/json")) { - data = await response.json(); - } else { - data = await response.text(); - } - - if (!response.ok) { - const errorData = data as { code?: string; message?: string }; - return { - success: false, - error: { - status: response.status, - code: errorData?.code, - message: - errorData?.message || response.statusText || "Request failed", - details: data, - }, - }; - } - - return { success: true, data: data as T }; - } catch (err) { - if (err instanceof Error && err.name === "AbortError") { - return { - success: false, - error: { - status: 0, - message: `Request timed out after ${this.timeoutMs}ms`, - }, - }; - } - const message = err instanceof Error ? err.message : "Unknown error"; - return { - success: false, - error: { - status: 0, - message: `Network error: ${message}`, - }, - }; - } - } - - async get( - path: string, - params?: Record - ): Promise> { - return this.request("GET", path, { params }); - } - - async post(path: string, body?: unknown): Promise> { - return this.request("POST", path, { body }); - } - - async patch(path: string, body?: unknown): Promise> { - return this.request("PATCH", path, { body }); - } - - async delete(path: string): Promise> { - return this.request("DELETE", path); - } -} diff --git a/cli/src/commands/accounts.ts b/cli/src/commands/accounts.ts deleted file mode 100644 index 98dfd332..00000000 --- a/cli/src/commands/accounts.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Command } from "commander"; -import { GridClient, PaginatedResponse } from "../client"; -import { outputResponse, formatError, output } from "../output"; -import { GlobalOptions } from "../index"; -import { validateCurrency, validateDate, validateAll } from "../validation"; - -interface InternalAccount { - id: string; - customerId?: string; - currency: string; - balance: number; - availableBalance: number; - status: string; - paymentInstructions?: unknown; - createdAt: string; - updatedAt: string; -} - -interface ExternalAccount { - id: string; - customerId: string; - currency: string; - accountInfo: { - accountType: string; - [key: string]: unknown; - }; - status: string; - createdAt: string; - updatedAt: string; -} - -export function registerAccountsCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const accountsCmd = program - .command("accounts") - .description("Account management commands"); - - const internalCmd = accountsCmd - .command("internal") - .description("Internal account commands"); - - internalCmd - .command("list") - .description("List internal accounts (balances)") - .option("-l, --limit ", "Maximum results (default 20, max 100)", "20") - .option("--cursor ", "Pagination cursor") - .option("--customer-id ", "Filter by customer ID") - .option("--currency ", "Filter by currency code") - .option("--platform", "List platform internal accounts instead of customer accounts") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - if (options.currency) { - const validation = validateCurrency(options.currency, "currency"); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - } - - const params: Record = { - limit: parseInt(options.limit, 10), - cursor: options.cursor, - customerId: options.customerId, - currency: options.currency, - }; - - const endpoint = options.platform - ? "/platform/internal-accounts" - : "/customers/internal-accounts"; - - const response = await client.get>( - endpoint, - params - ); - outputResponse(response); - }); - - const externalCmd = accountsCmd - .command("external") - .description("External account commands"); - - externalCmd - .command("list") - .description("List external accounts") - .option("-l, --limit ", "Maximum results (default 20, max 100)", "20") - .option("--cursor ", "Pagination cursor") - .option("--customer-id ", "Filter by customer ID") - .option("--currency ", "Filter by currency code") - .option("--platform", "List platform external accounts instead of customer accounts") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - if (options.currency) { - const validation = validateCurrency(options.currency, "currency"); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - } - - const params: Record = { - limit: parseInt(options.limit, 10), - cursor: options.cursor, - customerId: options.customerId, - currency: options.currency, - }; - - const endpoint = options.platform - ? "/platform/external-accounts" - : "/customers/external-accounts"; - - const response = await client.get>( - endpoint, - params - ); - outputResponse(response); - }); - - externalCmd - .command("create") - .description("Create an external account") - .requiredOption("--customer-id ", "Customer ID") - .requiredOption("--currency ", "Currency code (USD, MXN, BRL, EUR, etc.)") - .requiredOption("--account-type ", "Account type (US_ACCOUNT, CLABE, PIX, IBAN, UPI, NGN_ACCOUNT, SPARK_WALLET, etc.)") - .option("--account-number ", "Account number (for US_ACCOUNT, NGN_ACCOUNT)") - .option("--routing-number ", "Routing number (for US_ACCOUNT)") - .option("--account-category ", "Account category: CHECKING or SAVINGS (for US_ACCOUNT)") - .option("--clabe ", "CLABE number (for Mexico)") - .option("--pix-key ", "PIX key (for Brazil)") - .option("--iban ", "IBAN (for Europe)") - .option("--upi-id ", "UPI ID (for India)") - .option("--bank-name ", "Bank name (for NGN_ACCOUNT)") - .option("--purpose ", "Purpose of payment (for NGN_ACCOUNT): GIFT, SELF, GOODS_OR_SERVICES, EDUCATION, etc.") - .option("--address ", "Wallet address (for SPARK_WALLET, SOLANA_WALLET, etc.)") - .option("--beneficiary-type ", "Beneficiary type: INDIVIDUAL or BUSINESS") - .option("--beneficiary-name ", "Beneficiary full name (individual) or legal name (business)") - .option("--beneficiary-birth-date ", "Beneficiary birth date YYYY-MM-DD (individual)") - .option("--beneficiary-nationality ", "Beneficiary nationality country code (individual)") - .option("--beneficiary-address-line1 ", "Beneficiary address line 1") - .option("--beneficiary-address-city ", "Beneficiary city") - .option("--beneficiary-address-state ", "Beneficiary state") - .option("--beneficiary-address-postal ", "Beneficiary postal code") - .option("--beneficiary-address-country ", "Beneficiary country code") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const validations = [validateCurrency(options.currency, "currency")]; - if (options.beneficiaryBirthDate) { - validations.push(validateDate(options.beneficiaryBirthDate, "beneficiary-birth-date")); - } - const validation = validateAll(validations); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - - const accountInfo: Record = { - accountType: options.accountType, - }; - - switch (options.accountType) { - case "US_ACCOUNT": - if (options.accountNumber) accountInfo.accountNumber = options.accountNumber; - if (options.routingNumber) accountInfo.routingNumber = options.routingNumber; - if (options.accountCategory) accountInfo.accountCategory = options.accountCategory; - break; - case "CLABE": - if (options.clabe) accountInfo.clabeNumber = options.clabe; - break; - case "PIX": - if (options.pixKey) accountInfo.pixKey = options.pixKey; - break; - case "IBAN": - if (options.iban) accountInfo.iban = options.iban; - break; - case "UPI": - if (options.upiId) accountInfo.vpa = options.upiId; - break; - case "NGN_ACCOUNT": - if (options.accountNumber) accountInfo.accountNumber = options.accountNumber; - if (options.bankName) accountInfo.bankName = options.bankName; - if (options.purpose) accountInfo.purposeOfPayment = options.purpose; - break; - case "SPARK_WALLET": - case "SOLANA_WALLET": - case "TRON_WALLET": - case "POLYGON_WALLET": - case "BASE_WALLET": - if (options.address) accountInfo.address = options.address; - break; - } - - if (options.beneficiaryType || options.beneficiaryName) { - const beneficiary: Record = {}; - if (options.beneficiaryType) beneficiary.beneficiaryType = options.beneficiaryType; - - if (options.beneficiaryType === "INDIVIDUAL") { - if (options.beneficiaryName) beneficiary.fullName = options.beneficiaryName; - if (options.beneficiaryBirthDate) beneficiary.birthDate = options.beneficiaryBirthDate; - if (options.beneficiaryNationality) beneficiary.nationality = options.beneficiaryNationality; - } else if (options.beneficiaryType === "BUSINESS") { - if (options.beneficiaryName) beneficiary.legalName = options.beneficiaryName; - } - - if (options.beneficiaryAddressLine1 || options.beneficiaryAddressCity) { - beneficiary.address = { - line1: options.beneficiaryAddressLine1, - city: options.beneficiaryAddressCity, - state: options.beneficiaryAddressState, - postalCode: options.beneficiaryAddressPostal, - country: options.beneficiaryAddressCountry, - }; - } - - accountInfo.beneficiary = beneficiary; - } - - const body = { - customerId: options.customerId, - currency: options.currency, - accountInfo, - }; - - const response = await client.post( - "/customers/external-accounts", - body - ); - outputResponse(response); - }); -} diff --git a/cli/src/commands/config.ts b/cli/src/commands/config.ts deleted file mode 100644 index 8638f207..00000000 --- a/cli/src/commands/config.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Command } from "commander"; -import { GridClient } from "../client"; -import { outputResponse } from "../output"; -import { GlobalOptions } from "../index"; - -interface PlatformConfig { - id: string; - umaDomain?: string; - webhookEndpoint?: string; - supportedCurrencies: Array<{ - currencyCode: string; - minAmount?: number; - maxAmount?: number; - enabledTransactionTypes?: string[]; - }>; -} - -export function registerConfigCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const configCmd = program - .command("config") - .description("Platform configuration commands"); - - configCmd - .command("get") - .description("Get platform configuration (currencies, limits, webhook)") - .action(async () => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.get("/config"); - outputResponse(response); - }); - - configCmd - .command("update") - .description("Update platform configuration") - .option("--uma-domain ", "UMA domain") - .option("--webhook-endpoint ", "Webhook endpoint URL") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const body: Record = {}; - if (options.umaDomain) body.umaDomain = options.umaDomain; - if (options.webhookEndpoint) body.webhookEndpoint = options.webhookEndpoint; - - const response = await client.patch("/config", body); - outputResponse(response); - }); -} diff --git a/cli/src/commands/configure.ts b/cli/src/commands/configure.ts deleted file mode 100644 index ca38f99c..00000000 --- a/cli/src/commands/configure.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Command } from "commander"; -import * as readline from "readline"; -import { saveCredentials, getCredentialsPath } from "../config"; -import { formatSuccess, formatError, output } from "../output"; - -function prompt(question: string, hidden = false): Promise { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise((resolve) => { - if (hidden && process.stdin.isTTY) { - process.stdout.write(question); - const stdin = process.stdin; - stdin.setRawMode(true); - stdin.resume(); - stdin.setEncoding("utf8"); - - let input = ""; - const onData = (char: string) => { - if (char === "\n" || char === "\r" || char === "\u0004") { - stdin.setRawMode(false); - stdin.removeListener("data", onData); - rl.close(); - process.stdout.write("\n"); - resolve(input); - } else if (char === "\u0003") { - process.exit(); - } else if (char === "\u007F" || char === "\b") { - if (input.length > 0) { - input = input.slice(0, -1); - } - } else { - input += char; - } - }; - stdin.on("data", onData); - } else { - rl.question(question, (answer) => { - rl.close(); - resolve(answer); - }); - } - }); -} - -async function testCredentials(apiTokenId: string, apiClientSecret: string, baseUrl: string): Promise { - const credentials = `${apiTokenId}:${apiClientSecret}`; - const auth = `Basic ${Buffer.from(credentials).toString("base64")}`; - - try { - const response = await fetch(`${baseUrl}/config`, { - headers: { Authorization: auth, Accept: "application/json" }, - }); - return response.ok; - } catch { - return false; - } -} - -export function registerConfigureCommand(program: Command): void { - program - .command("configure") - .description("Configure Grid API credentials interactively") - .option("--token-id ", "API token ID (skip prompt)") - .option("--client-secret ", "API client secret (skip prompt)") - .option("--base-url ", "API base URL") - .option("--no-verify", "Skip credential verification") - .action(async (options) => { - const credentialsPath = getCredentialsPath(); - console.log(`\nGrid CLI Configuration\n`); - console.log(`Credentials will be saved to: ${credentialsPath}\n`); - - let apiTokenId = options.tokenId; - let apiClientSecret = options.clientSecret; - const baseUrl = options.baseUrl || "https://api.lightspark.com/grid/2025-10-13"; - - if (!apiTokenId) { - apiTokenId = await prompt("API Token ID: "); - } - if (!apiClientSecret) { - apiClientSecret = await prompt("API Client Secret: ", true); - } - - if (!apiTokenId || !apiClientSecret) { - output(formatError("API Token ID and Client Secret are required")); - process.exitCode = 1; - return; - } - - if (options.verify !== false) { - process.stdout.write("Verifying credentials... "); - const valid = await testCredentials(apiTokenId, apiClientSecret, baseUrl); - if (!valid) { - console.log("FAILED"); - output(formatError("Credentials verification failed. Check your API Token ID and Client Secret.")); - process.exitCode = 1; - return; - } - console.log("OK"); - } - - saveCredentials({ apiTokenId, apiClientSecret, baseUrl }); - output(formatSuccess({ - message: "Configuration saved successfully", - credentialsPath, - })); - }); -} diff --git a/cli/src/commands/customers.ts b/cli/src/commands/customers.ts deleted file mode 100644 index 8e122826..00000000 --- a/cli/src/commands/customers.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { Command } from "commander"; -import { GridClient, PaginatedResponse } from "../client"; -import { outputResponse, formatError, output } from "../output"; -import { GlobalOptions } from "../index"; -import { validateDate, validateCustomerType, validateAll } from "../validation"; -import { confirm } from "../prompt"; - -interface Customer { - id: string; - platformCustomerId: string; - customerType: "INDIVIDUAL" | "BUSINESS"; - umaAddress?: string; - fullName?: string; - birthDate?: string; - kycStatus?: string; - createdAt: string; - updatedAt: string; -} - -export function registerCustomersCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const customersCmd = program - .command("customers") - .description("Customer management commands"); - - customersCmd - .command("list") - .description("List customers") - .option("-l, --limit ", "Maximum results (default 20, max 100)", "20") - .option("--cursor ", "Pagination cursor") - .option("--platform-id ", "Filter by platform customer ID") - .option("--type ", "Filter by type (INDIVIDUAL or BUSINESS)") - .option("--uma-address
", "Filter by UMA address") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const params: Record = { - limit: parseInt(options.limit, 10), - cursor: options.cursor, - platformCustomerId: options.platformId, - customerType: options.type, - umaAddress: options.umaAddress, - }; - - const response = await client.get>( - "/customers", - params - ); - outputResponse(response); - }); - - customersCmd - .command("get ") - .description("Get customer details") - .action(async (customerId: string) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.get(`/customers/${customerId}`); - outputResponse(response); - }); - - customersCmd - .command("create") - .description("Create a new customer") - .requiredOption("--platform-id ", "Platform-specific customer ID") - .option("--type ", "Customer type (INDIVIDUAL or BUSINESS)", "INDIVIDUAL") - .option("--uma-address
", "UMA address (optional, generated if not provided)") - .option("--full-name ", "Full name (for individuals)") - .option("--birth-date ", "Birth date YYYY-MM-DD (for individuals)") - .option("--legal-name ", "Legal name (for businesses)") - .option("--registration-number ", "Registration number (for businesses)") - .option("--tax-id ", "Tax ID (for businesses)") - .option("--address-line1 ", "Address line 1") - .option("--address-city ", "City") - .option("--address-state ", "State/Province") - .option("--address-postal ", "Postal code") - .option("--address-country ", "Country code (e.g., US)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const validations = [validateCustomerType(options.type)]; - if (options.birthDate) { - validations.push(validateDate(options.birthDate, "birth-date")); - } - const validation = validateAll(validations); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - - const body: Record = { - platformCustomerId: options.platformId, - customerType: options.type, - }; - - if (options.umaAddress) body.umaAddress = options.umaAddress; - - if (options.type === "INDIVIDUAL") { - if (options.fullName) body.fullName = options.fullName; - if (options.birthDate) body.birthDate = options.birthDate; - } else if (options.type === "BUSINESS") { - const businessInfo: Record = {}; - if (options.legalName) businessInfo.legalName = options.legalName; - if (options.registrationNumber) - businessInfo.registrationNumber = options.registrationNumber; - if (options.taxId) businessInfo.taxId = options.taxId; - body.businessInfo = businessInfo; - } - - if (options.addressLine1 || options.addressCity) { - body.address = { - line1: options.addressLine1, - city: options.addressCity, - state: options.addressState, - postalCode: options.addressPostal, - country: options.addressCountry, - }; - } - - const response = await client.post("/customers", body); - outputResponse(response); - }); - - customersCmd - .command("update ") - .description("Update a customer") - .option("--full-name ", "Full name") - .option("--birth-date ", "Birth date YYYY-MM-DD") - .option("--address-line1 ", "Address line 1") - .option("--address-city ", "City") - .option("--address-state ", "State/Province") - .option("--address-postal ", "Postal code") - .option("--address-country ", "Country code") - .action(async (customerId: string, options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - if (options.birthDate) { - const validation = validateDate(options.birthDate, "birth-date"); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - } - - const body: Record = {}; - if (options.fullName) body.fullName = options.fullName; - if (options.birthDate) body.birthDate = options.birthDate; - - if (options.addressLine1 || options.addressCity) { - body.address = { - line1: options.addressLine1, - city: options.addressCity, - state: options.addressState, - postalCode: options.addressPostal, - country: options.addressCountry, - }; - } - - const response = await client.patch( - `/customers/${customerId}`, - body - ); - outputResponse(response); - }); - - customersCmd - .command("delete ") - .description("Delete a customer") - .option("-y, --yes", "Skip confirmation prompt") - .action(async (customerId: string, options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - if (!options.yes) { - const confirmed = await confirm( - `Are you sure you want to delete customer ${customerId}? This action cannot be undone.` - ); - if (!confirmed) { - console.log("Aborted."); - return; - } - } - - const response = await client.delete(`/customers/${customerId}`); - outputResponse(response); - }); - - customersCmd - .command("kyc-link") - .description("Generate a KYC link for a customer") - .requiredOption("--customer-id ", "Customer ID") - .requiredOption("--redirect-url ", "Redirect URL after KYC completion") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const body = { - customerId: options.customerId, - redirectUrl: options.redirectUrl, - }; - - const response = await client.post<{ kycUrl: string }>( - "/customers/kyc-link", - body - ); - outputResponse(response); - }); -} diff --git a/cli/src/commands/quotes.ts b/cli/src/commands/quotes.ts deleted file mode 100644 index 691c91b9..00000000 --- a/cli/src/commands/quotes.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { Command } from "commander"; -import { GridClient, PaginatedResponse } from "../client"; -import { outputResponse, formatError, output } from "../output"; -import { GlobalOptions } from "../index"; -import { - validateAmount, - validateLockSide, - validateDate, - validateCurrency, - validateAll, - parseAmount, -} from "../validation"; - -interface Quote { - id: string; - status: "PENDING" | "PROCESSING" | "COMPLETED" | "FAILED" | "EXPIRED"; - source: { - accountId?: string; - customerId?: string; - currency: string; - }; - destination: { - accountId?: string; - umaAddress?: string; - currency: string; - }; - lockedCurrencySide: "SENDING" | "RECEIVING"; - lockedCurrencyAmount: number; - sendingAmount: number; - sendingCurrency: string; - receivingAmount: number; - receivingCurrency: string; - exchangeRate: number; - fees?: Array<{ - type: string; - amount: number; - currency: string; - }>; - expiresAt: string; - createdAt: string; - updatedAt: string; -} - -export function registerQuotesCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const quotesCmd = program - .command("quotes") - .description("Quote management commands"); - - quotesCmd - .command("list") - .description("List transfer quotes") - .option("-l, --limit ", "Maximum results (default 20, max 100)", "20") - .option("--cursor ", "Pagination cursor") - .option("--customer-id ", "Filter by sending customer ID") - .option("--sending-account ", "Filter by sending account ID") - .option("--receiving-account ", "Filter by receiving account ID") - .option("--sending-uma
", "Filter by sending UMA address") - .option("--receiving-uma
", "Filter by receiving UMA address") - .option("--status ", "Filter by status (PENDING, PROCESSING, COMPLETED, FAILED, EXPIRED)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const params: Record = { - limit: parseInt(options.limit, 10), - cursor: options.cursor, - customerId: options.customerId, - sendingAccountId: options.sendingAccount, - receivingAccountId: options.receivingAccount, - sendingUmaAddress: options.sendingUma, - receivingUmaAddress: options.receivingUma, - status: options.status, - }; - - const response = await client.get>( - "/quotes", - params - ); - outputResponse(response); - }); - - quotesCmd - .command("get ") - .description("Get quote details") - .action(async (quoteId: string) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.get(`/quotes/${quoteId}`); - outputResponse(response); - }); - - quotesCmd - .command("create") - .description("Create a transfer quote") - .requiredOption("--amount ", "Amount in smallest currency unit (e.g., cents)") - .requiredOption("--lock-side ", "Lock SENDING or RECEIVING amount") - .option("--source-account ", "Source account ID (InternalAccount:...)") - .option("--source-customer ", "Source customer ID (for customer-funded quotes)") - .option("--source-currency ", "Source currency (required with --source-customer)") - .option("--dest-account ", "Destination account ID") - .option("--dest-uma
", "Destination UMA address") - .option("--dest-currency ", "Destination currency") - .option("--description ", "Transfer description") - .option("--lookup-id ", "Lookup request ID (from receiver lookup)") - .option("--immediate", "Execute the quote immediately after creation") - .option("--sender-name ", "Sender full name (for UMA destinations)") - .option("--sender-birth-date ", "Sender birth date YYYY-MM-DD (for UMA destinations)") - .option("--sender-nationality ", "Sender nationality country code (for UMA destinations)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const validations = [ - validateAmount(options.amount, "amount"), - validateLockSide(options.lockSide), - ]; - if (options.sourceCurrency) { - validations.push(validateCurrency(options.sourceCurrency, "source-currency")); - } - if (options.destCurrency) { - validations.push(validateCurrency(options.destCurrency, "dest-currency")); - } - if (options.senderBirthDate) { - validations.push(validateDate(options.senderBirthDate, "sender-birth-date")); - } - const validation = validateAll(validations); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - - const body: Record = { - lockedCurrencyAmount: parseAmount(options.amount), - lockedCurrencySide: options.lockSide, - }; - - if (options.sourceAccount) { - body.source = { - sourceType: "ACCOUNT", - accountId: options.sourceAccount, - }; - } else if (options.sourceCustomer) { - body.source = { - sourceType: "REALTIME_FUNDING", - customerId: options.sourceCustomer, - currency: options.sourceCurrency, - }; - } - - if (options.destAccount) { - body.destination = { - destinationType: "ACCOUNT", - accountId: options.destAccount, - }; - } else if (options.destUma) { - body.destination = { - destinationType: "UMA_ADDRESS", - umaAddress: options.destUma, - currency: options.destCurrency, - }; - } - - if (options.description) body.description = options.description; - if (options.lookupId) body.lookupId = options.lookupId; - if (options.immediate) body.immediatelyExecute = true; - - const senderInfo: Record = {}; - if (options.senderName) senderInfo.FULL_NAME = options.senderName; - if (options.senderBirthDate) senderInfo.BIRTH_DATE = options.senderBirthDate; - if (options.senderNationality) senderInfo.NATIONALITY = options.senderNationality; - if (Object.keys(senderInfo).length > 0) { - body.senderCustomerInfo = senderInfo; - } - - const response = await client.post("/quotes", body); - outputResponse(response); - }); - - quotesCmd - .command("execute ") - .description("Execute a pending quote") - .action(async (quoteId: string) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.post(`/quotes/${quoteId}/execute`); - outputResponse(response); - }); -} diff --git a/cli/src/commands/receiver.ts b/cli/src/commands/receiver.ts deleted file mode 100644 index c820c3a8..00000000 --- a/cli/src/commands/receiver.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Command } from "commander"; -import { GridClient } from "../client"; -import { outputResponse } from "../output"; -import { GlobalOptions } from "../index"; - -interface ReceiverLookup { - id: string; - umaAddress?: string; - accountId?: string; - currencies: Array<{ - code: string; - name: string; - symbol: string; - minAmount?: number; - maxAmount?: number; - }>; - requiredPayerData?: Array<{ - name: string; - mandatory: boolean; - }>; -} - -export function registerReceiverCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const receiverCmd = program - .command("receiver") - .description("Receiver lookup commands"); - - receiverCmd - .command("lookup-uma ") - .description("Look up an UMA address to get payment capabilities") - .option("--customer-id ", "Sender customer ID") - .option("--sender-uma
", "Sender UMA address") - .action(async (umaAddress: string, options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const params: Record = { - customerId: options.customerId, - senderUmaAddress: options.senderUma, - }; - - const response = await client.get( - `/receiver/uma/${encodeURIComponent(umaAddress)}`, - params - ); - outputResponse(response); - }); - - receiverCmd - .command("lookup-account ") - .description("Look up an external account to get payment capabilities") - .action(async (accountId: string) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.get( - `/receiver/external-account/${encodeURIComponent(accountId)}` - ); - outputResponse(response); - }); -} diff --git a/cli/src/commands/sandbox.ts b/cli/src/commands/sandbox.ts deleted file mode 100644 index 7e4e039d..00000000 --- a/cli/src/commands/sandbox.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Command } from "commander"; -import { GridClient } from "../client"; -import { outputResponse, formatError, output } from "../output"; -import { GlobalOptions } from "../index"; -import { validateAmount, validateCurrency, validateAll, parseAmount } from "../validation"; - -export function registerSandboxCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const sandboxCmd = program - .command("sandbox") - .description("Sandbox testing commands"); - - sandboxCmd - .command("send") - .description("Simulate sending a payment in sandbox") - .requiredOption("--quote-id ", "Quote ID to simulate sending") - .requiredOption("--currency ", "Currency code for the funds to send") - .option("--amount ", "Amount in smallest unit (derived from quote if not provided)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const validations = [validateCurrency(options.currency, "currency")]; - if (options.amount) { - validations.push(validateAmount(options.amount, "amount")); - } - const validation = validateAll(validations); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - - const body: Record = { - quoteId: options.quoteId, - currencyCode: options.currency, - }; - if (options.amount) body.currencyAmount = parseAmount(options.amount); - const response = await client.post("/sandbox/send", body); - outputResponse(response); - }); - - sandboxCmd - .command("receive") - .description("Simulate receiving an UMA payment in sandbox") - .requiredOption("--uma-address
", "Receiver UMA address") - .requiredOption("--amount ", "Amount in smallest unit") - .requiredOption("--currency ", "Currency code") - .option("--sender-uma
", "Sender UMA address") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const validation = validateAll([ - validateAmount(options.amount, "amount"), - validateCurrency(options.currency, "currency"), - ]); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - - const body: Record = { - receiverUmaAddress: options.umaAddress, - amount: parseAmount(options.amount), - currency: options.currency, - }; - if (options.senderUma) body.senderUmaAddress = options.senderUma; - - const response = await client.post("/sandbox/uma/receive", body); - outputResponse(response); - }); - - sandboxCmd - .command("fund ") - .description("Fund an internal account in sandbox") - .requiredOption("--amount ", "Amount in smallest unit") - .action(async (accountId: string, options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const validation = validateAmount(options.amount, "amount"); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - - const body = { amount: parseAmount(options.amount) }; - const response = await client.post( - `/sandbox/internal-accounts/${accountId}/fund`, - body - ); - outputResponse(response); - }); -} diff --git a/cli/src/commands/transactions.ts b/cli/src/commands/transactions.ts deleted file mode 100644 index cbd449fc..00000000 --- a/cli/src/commands/transactions.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Command } from "commander"; -import { GridClient, PaginatedResponse } from "../client"; -import { outputResponse } from "../output"; -import { GlobalOptions } from "../index"; - -interface Transaction { - id: string; - type: "INCOMING" | "OUTGOING"; - status: string; - amount: number; - currency: string; - senderAccountIdentifier?: string; - receiverAccountIdentifier?: string; - reference?: string; - description?: string; - createdAt: string; - updatedAt: string; -} - -export function registerTransactionsCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const transactionsCmd = program - .command("transactions") - .description("Transaction management commands"); - - transactionsCmd - .command("list") - .description("List transactions") - .option("-l, --limit ", "Maximum results (default 20, max 100)", "20") - .option("--cursor ", "Pagination cursor") - .option("--customer-id ", "Filter by customer ID") - .option("--platform-customer-id ", "Filter by platform customer ID") - .option("--sender ", "Filter by sender account identifier") - .option("--receiver ", "Filter by receiver account identifier") - .option("--status ", "Filter by status") - .option("--type ", "Filter by type (INCOMING or OUTGOING)") - .option("--reference ", "Filter by reference") - .option("--start-date ", "Filter by start date (ISO 8601)") - .option("--end-date ", "Filter by end date (ISO 8601)") - .option("--sort ", "Sort order: asc or desc (default: desc)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const params: Record = { - limit: parseInt(options.limit, 10), - cursor: options.cursor, - customerId: options.customerId, - platformCustomerId: options.platformCustomerId, - senderAccountIdentifier: options.sender, - receiverAccountIdentifier: options.receiver, - status: options.status, - type: options.type, - reference: options.reference, - startDate: options.startDate, - endDate: options.endDate, - sortOrder: options.sort, - }; - - const response = await client.get>( - "/transactions", - params - ); - outputResponse(response); - }); - - transactionsCmd - .command("get ") - .description("Get transaction details") - .action(async (transactionId: string) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.get( - `/transactions/${transactionId}` - ); - outputResponse(response); - }); - - transactionsCmd - .command("approve ") - .description("Approve an incoming payment transaction") - .action(async (transactionId: string) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const response = await client.post( - `/transactions/${transactionId}/approve` - ); - outputResponse(response); - }); - - transactionsCmd - .command("reject ") - .description("Reject an incoming payment transaction") - .option("--reason ", "Rejection reason") - .action(async (transactionId: string, options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - const body = options.reason ? { reason: options.reason } : undefined; - const response = await client.post( - `/transactions/${transactionId}/reject`, - body - ); - outputResponse(response); - }); -} diff --git a/cli/src/commands/transfers.ts b/cli/src/commands/transfers.ts deleted file mode 100644 index bfd324c5..00000000 --- a/cli/src/commands/transfers.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Command } from "commander"; -import { GridClient } from "../client"; -import { outputResponse, formatError, output } from "../output"; -import { GlobalOptions } from "../index"; -import { validateAmount, parseAmount } from "../validation"; - -interface Transaction { - id: string; - type: "INCOMING" | "OUTGOING"; - status: string; - amount: number; - currency: string; - createdAt: string; - updatedAt: string; -} - -export function registerTransfersCommand( - program: Command, - getClient: (opts: GlobalOptions) => GridClient | null -): void { - const transfersCmd = program - .command("transfers") - .description("Same-currency transfer commands"); - - transfersCmd - .command("in") - .description("Transfer from external account to internal account (same currency)") - .requiredOption("--source ", "Source external account ID (ExternalAccount:...)") - .requiredOption("--dest ", "Destination internal account ID (InternalAccount:...)") - .option("--amount ", "Amount in smallest currency unit (optional for full balance)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - if (options.amount) { - const validation = validateAmount(options.amount, "amount"); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - } - - const body: Record = { - source: { accountId: options.source }, - destination: { accountId: options.dest }, - }; - - if (options.amount) { - body.amount = parseAmount(options.amount); - } - - const response = await client.post("/transfer-in", body); - outputResponse(response); - }); - - transfersCmd - .command("out") - .description("Transfer from internal account to external account (same currency)") - .requiredOption("--source ", "Source internal account ID (InternalAccount:...)") - .requiredOption("--dest ", "Destination external account ID (ExternalAccount:...)") - .option("--amount ", "Amount in smallest currency unit (optional for full balance)") - .action(async (options) => { - const opts = program.opts(); - const client = getClient(opts); - if (!client) return; - - if (options.amount) { - const validation = validateAmount(options.amount, "amount"); - if (!validation.valid) { - output(formatError(validation.error!)); - process.exitCode = 1; - return; - } - } - - const body: Record = { - source: { accountId: options.source }, - destination: { accountId: options.dest }, - }; - - if (options.amount) { - body.amount = parseAmount(options.amount); - } - - const response = await client.post("/transfer-out", body); - outputResponse(response); - }); -} diff --git a/cli/src/config.ts b/cli/src/config.ts deleted file mode 100644 index a6941532..00000000 --- a/cli/src/config.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as os from "os"; - -export interface GridConfig { - apiTokenId: string; - apiClientSecret: string; - baseUrl: string; -} - -const DEFAULT_BASE_URL = "https://api.lightspark.com/grid/2025-10-13"; -const CREDENTIALS_FILE = ".grid-credentials"; - -function getCredentialsPath(): string { - return path.join(os.homedir(), CREDENTIALS_FILE); -} - -function loadCredentialsFile(): Partial { - const credentialsPath = getCredentialsPath(); - if (fs.existsSync(credentialsPath)) { - const content = fs.readFileSync(credentialsPath, "utf-8"); - try { - return JSON.parse(content); - } catch { - throw new Error( - `Invalid JSON in credentials file: ${credentialsPath}. ` + - `Please fix the file or delete it and run 'grid configure'.` - ); - } - } - return {}; -} - -export { getCredentialsPath }; - -export function loadConfig(options: { - configPath?: string; - baseUrl?: string; -}): GridConfig { - let fileConfig: Partial = {}; - - if (options.configPath) { - if (!fs.existsSync(options.configPath)) { - throw new Error(`Config file not found: ${options.configPath}`); - } - const content = fs.readFileSync(options.configPath, "utf-8"); - try { - fileConfig = JSON.parse(content); - } catch { - throw new Error(`Invalid JSON in config file: ${options.configPath}`); - } - } else { - fileConfig = loadCredentialsFile(); - } - - const apiTokenId = - process.env.GRID_API_TOKEN_ID || fileConfig.apiTokenId || ""; - const apiClientSecret = - process.env.GRID_API_CLIENT_SECRET || fileConfig.apiClientSecret || ""; - const baseUrl = - options.baseUrl || - process.env.GRID_BASE_URL || - fileConfig.baseUrl || - DEFAULT_BASE_URL; - - if (!apiTokenId || !apiClientSecret) { - throw new Error( - `Missing credentials. Set GRID_API_TOKEN_ID and GRID_API_CLIENT_SECRET environment variables, ` + - `or create ${getCredentialsPath()} with apiTokenId and apiClientSecret fields.` - ); - } - - return { apiTokenId, apiClientSecret, baseUrl }; -} - -export function saveCredentials(config: Partial): void { - const credentialsPath = getCredentialsPath(); - let existing: Partial = {}; - - if (fs.existsSync(credentialsPath)) { - const content = fs.readFileSync(credentialsPath, "utf-8"); - try { - existing = JSON.parse(content); - } catch { - existing = {}; - } - } - - const merged = { ...existing, ...config }; - fs.writeFileSync(credentialsPath, JSON.stringify(merged, null, 2), { - mode: 0o600, - }); -} diff --git a/cli/src/index.ts b/cli/src/index.ts deleted file mode 100644 index 2bc74f27..00000000 --- a/cli/src/index.ts +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env node - -import { Command } from "commander"; -import * as fs from "fs"; -import * as path from "path"; -import { loadConfig, GridConfig } from "./config"; -import { GridClient } from "./client"; -import { formatError, output, setOutputFormat, setUseColors, OutputFormat } from "./output"; - -export interface GlobalOptions { - config?: string; - baseUrl?: string; - format?: OutputFormat; - color?: boolean; -} - -const packageJson = JSON.parse( - fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8") -); - -const program = new Command(); - -program - .name("grid") - .description("CLI for Grid API - manage global payments") - .version(packageJson.version) - .option("-c, --config ", "Path to credentials file") - .option( - "-u, --base-url ", - "Base URL for API (default: https://api.lightspark.com/grid/2025-10-13)" - ) - .option("-f, --format ", "Output format: json or table", "json") - .option("--no-color", "Disable colored output") - .hook("preAction", (thisCommand) => { - const opts = thisCommand.opts(); - if (opts.format) setOutputFormat(opts.format as OutputFormat); - if (opts.color === false) setUseColors(false); - }); - -function getClient(options: GlobalOptions): GridClient | null { - try { - const config = loadConfig({ - configPath: options.config, - baseUrl: options.baseUrl, - }); - return new GridClient(config); - } catch (err) { - const message = err instanceof Error ? err.message : "Configuration error"; - output(formatError(message)); - process.exitCode = 1; - return null; - } -} - -export { program, getClient, GridClient, GridConfig }; - -async function main() { - const { registerConfigureCommand } = await import("./commands/configure"); - const { registerConfigCommand } = await import("./commands/config"); - const { registerCustomersCommand } = await import("./commands/customers"); - const { registerAccountsCommand } = await import("./commands/accounts"); - const { registerQuotesCommand } = await import("./commands/quotes"); - const { registerTransactionsCommand } = await import( - "./commands/transactions" - ); - const { registerTransfersCommand } = await import("./commands/transfers"); - const { registerSandboxCommand } = await import("./commands/sandbox"); - const { registerReceiverCommand } = await import("./commands/receiver"); - - registerConfigureCommand(program); - registerConfigCommand(program, getClient); - registerCustomersCommand(program, getClient); - registerAccountsCommand(program, getClient); - registerQuotesCommand(program, getClient); - registerTransactionsCommand(program, getClient); - registerTransfersCommand(program, getClient); - registerSandboxCommand(program, getClient); - registerReceiverCommand(program, getClient); - - const customersCmd = program.commands.find(c => c.name() === "customers"); - const transactionsCmd = program.commands.find(c => c.name() === "transactions"); - const accountsCmd = program.commands.find(c => c.name() === "accounts"); - - if (customersCmd) customersCmd.alias("cust"); - if (transactionsCmd) transactionsCmd.alias("tx"); - if (accountsCmd) accountsCmd.alias("acct"); - - await program.parseAsync(process.argv); -} - -main().catch((err) => { - output(formatError(err.message)); - process.exitCode = 1; -}); diff --git a/cli/src/output.ts b/cli/src/output.ts deleted file mode 100644 index 98b86ec1..00000000 --- a/cli/src/output.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { ApiResponse } from "./client"; - -export type OutputFormat = "json" | "table"; - -let currentFormat: OutputFormat = "json"; -let useColors = process.stdout.isTTY ?? false; - -export function setOutputFormat(format: OutputFormat): void { - currentFormat = format; -} - -export function setUseColors(colors: boolean): void { - useColors = colors; -} - -const colors = { - reset: "\x1b[0m", - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", - blue: "\x1b[34m", - cyan: "\x1b[36m", - dim: "\x1b[2m", -}; - -function colorize(text: string, color: keyof typeof colors): string { - if (!useColors) return text; - return `${colors[color]}${text}${colors.reset}`; -} - -export interface CliOutput { - success: boolean; - data?: T; - error?: { - code?: string; - message: string; - details?: unknown; - }; -} - -function colorizeJson(json: string): string { - if (!useColors) return json; - return json - .replace(/"([^"]+)":/g, `${colors.cyan}"$1"${colors.reset}:`) - .replace(/: "([^"]+)"/g, `: ${colors.green}"$1"${colors.reset}`) - .replace(/: (\d+)/g, `: ${colors.yellow}$1${colors.reset}`) - .replace(/: (true|false)/g, `: ${colors.blue}$1${colors.reset}`) - .replace(/: (null)/g, `: ${colors.dim}$1${colors.reset}`); -} - -function formatAsTable(data: T): string { - if (Array.isArray(data)) { - if (data.length === 0) return colorize("No results", "dim"); - const items = data as Record[]; - const keys = Object.keys(items[0]).filter(k => - typeof items[0][k] !== "object" || items[0][k] === null - ); - const widths = keys.map(k => - Math.max(k.length, ...items.map(item => String(item[k] ?? "").length)) - ); - const header = keys.map((k, i) => k.padEnd(widths[i])).join(" "); - const separator = widths.map(w => "-".repeat(w)).join(" "); - const rows = items.map(item => - keys.map((k, i) => String(item[k] ?? "").padEnd(widths[i])).join(" ") - ); - return [colorize(header, "cyan"), colorize(separator, "dim"), ...rows].join("\n"); - } - if (typeof data === "object" && data !== null) { - const obj = data as Record; - const maxKeyLen = Math.max(...Object.keys(obj).map(k => k.length)); - return Object.entries(obj) - .filter(([, v]) => typeof v !== "object" || v === null) - .map(([k, v]) => `${colorize(k.padEnd(maxKeyLen), "cyan")} ${v}`) - .join("\n"); - } - return String(data); -} - -export function formatOutput(response: ApiResponse): string { - if (currentFormat === "table") { - if (!response.success) { - const err = response.error; - return colorize(`Error: ${err?.message || "Unknown error"}`, "red") + - (err?.code ? colorize(` (${err.code})`, "dim") : ""); - } - const data = response.data; - if (data && typeof data === "object" && "data" in data) { - return formatAsTable((data as { data: unknown }).data); - } - return formatAsTable(data); - } - - const output: CliOutput = { - success: response.success, - }; - - if (response.success) { - output.data = response.data; - } else if (response.error) { - output.error = { - code: response.error.code, - message: response.error.message, - details: response.error.details, - }; - } - - return colorizeJson(JSON.stringify(output, null, 2)); -} - -export function formatError(message: string, details?: unknown): string { - const output: CliOutput = { - success: false, - error: { message, details }, - }; - return JSON.stringify(output, null, 2); -} - -export function formatSuccess(data: T): string { - const output: CliOutput = { - success: true, - data, - }; - return JSON.stringify(output, null, 2); -} - -export function output(result: string): void { - console.log(result); -} - -export function outputResponse(response: ApiResponse): void { - output(formatOutput(response)); - if (!response.success) { - process.exitCode = 1; - } -} diff --git a/cli/src/prompt.ts b/cli/src/prompt.ts deleted file mode 100644 index ae01982d..00000000 --- a/cli/src/prompt.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as readline from "readline"; - -export async function confirm(message: string, defaultValue = false): Promise { - const suffix = defaultValue ? "[Y/n]" : "[y/N]"; - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise((resolve) => { - rl.question(`${message} ${suffix} `, (answer) => { - rl.close(); - const normalized = answer.trim().toLowerCase(); - if (normalized === "") { - resolve(defaultValue); - } else { - resolve(normalized === "y" || normalized === "yes"); - } - }); - }); -} - -export async function promptInput(message: string): Promise { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise((resolve) => { - rl.question(message, (answer) => { - rl.close(); - resolve(answer.trim()); - }); - }); -} diff --git a/cli/src/validation.ts b/cli/src/validation.ts deleted file mode 100644 index c54e429e..00000000 --- a/cli/src/validation.ts +++ /dev/null @@ -1,94 +0,0 @@ -export interface ValidationResult { - valid: boolean; - error?: string; -} - -export function validateDate(value: string, fieldName: string): ValidationResult { - const dateRegex = /^\d{4}-\d{2}-\d{2}$/; - if (!dateRegex.test(value)) { - return { - valid: false, - error: `${fieldName} must be in YYYY-MM-DD format (got: ${value})`, - }; - } - const date = new Date(value); - if (isNaN(date.getTime())) { - return { - valid: false, - error: `${fieldName} is not a valid date (got: ${value})`, - }; - } - return { valid: true }; -} - -export function validateAmount(value: string, fieldName: string): ValidationResult { - const amount = parseInt(value, 10); - if (isNaN(amount)) { - return { - valid: false, - error: `${fieldName} must be a valid integer (got: ${value})`, - }; - } - if (amount < 0) { - return { - valid: false, - error: `${fieldName} must be non-negative (got: ${value})`, - }; - } - return { valid: true }; -} - -export function parseAmount(value: string): number { - const amount = parseInt(value, 10); - if (isNaN(amount)) { - throw new Error(`Invalid amount: ${value}`); - } - return amount; -} - -const VALID_CURRENCIES = new Set([ - "USD", "EUR", "GBP", "MXN", "BRL", "INR", "NGN", "PHP", "KES", - "BTC", "SAT", "USDC", "USDT", -]); - -export function validateCurrency(value: string, fieldName: string): ValidationResult { - const upper = value.toUpperCase(); - if (!VALID_CURRENCIES.has(upper)) { - return { - valid: false, - error: `${fieldName} "${value}" is not a recognized currency. Valid: ${Array.from(VALID_CURRENCIES).join(", ")}`, - }; - } - return { valid: true }; -} - -const VALID_CUSTOMER_TYPES = new Set(["INDIVIDUAL", "BUSINESS"]); - -export function validateCustomerType(value: string): ValidationResult { - if (!VALID_CUSTOMER_TYPES.has(value)) { - return { - valid: false, - error: `Customer type must be INDIVIDUAL or BUSINESS (got: ${value})`, - }; - } - return { valid: true }; -} - -const VALID_LOCK_SIDES = new Set(["SENDING", "RECEIVING"]); - -export function validateLockSide(value: string): ValidationResult { - if (!VALID_LOCK_SIDES.has(value)) { - return { - valid: false, - error: `Lock side must be SENDING or RECEIVING (got: ${value})`, - }; - } - return { valid: true }; -} - -export function validateAll(validations: ValidationResult[]): ValidationResult { - for (const v of validations) { - if (!v.valid) return v; - } - return { valid: true }; -} diff --git a/cli/tsconfig.json b/cli/tsconfig.json deleted file mode 100644 index 19517784..00000000 --- a/cli/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "commonjs", - "lib": ["ES2022"], - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..6680f9ce --- /dev/null +++ b/gradle.properties @@ -0,0 +1,18 @@ +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.parallel=true +org.gradle.daemon=false +# These options improve our compilation and test performance. They are inherited by the Kotlin daemon. +org.gradle.jvmargs=\ + -Xms2g \ + -Xmx8g \ + -XX:+UseParallelGC \ + -XX:InitialCodeCacheSize=256m \ + -XX:ReservedCodeCacheSize=1G \ + -XX:MetaspaceSize=512m \ + -XX:MaxMetaspaceSize=2G \ + -XX:TieredStopAtLevel=1 \ + -XX:GCTimeRatio=4 \ + -XX:CICompilerCount=4 \ + -XX:+OptimizeStringConcat \ + -XX:+UseStringDeduplication diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4b76b9530d66f5e68d973ea569d8e19de379189 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..cea7a793 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..f3b75f3b --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..9b42019c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/grid-kotlin-client-okhttp/build.gradle.kts b/grid-kotlin-client-okhttp/build.gradle.kts new file mode 100644 index 00000000..21b21c59 --- /dev/null +++ b/grid-kotlin-client-okhttp/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("grid.kotlin") + id("grid.publish") +} + +dependencies { + api(project(":grid-kotlin-core")) + + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") + + testImplementation(kotlin("test")) + testImplementation("org.assertj:assertj-core:3.27.7") +} diff --git a/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt b/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt new file mode 100644 index 00000000..9d9d2285 --- /dev/null +++ b/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClient.kt @@ -0,0 +1,323 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.client.okhttp + +import com.fasterxml.jackson.databind.json.JsonMapper +import com.grid.api.client.GridClient +import com.grid.api.client.GridClientImpl +import com.grid.api.core.ClientOptions +import com.grid.api.core.Sleeper +import com.grid.api.core.Timeout +import com.grid.api.core.http.Headers +import com.grid.api.core.http.HttpClient +import com.grid.api.core.http.QueryParams +import com.grid.api.core.jsonMapper +import java.net.Proxy +import java.time.Clock +import java.time.Duration +import java.util.concurrent.ExecutorService +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager + +/** + * A class that allows building an instance of [GridClient] with [OkHttpClient] as the underlying + * [HttpClient]. + */ +class GridOkHttpClient private constructor() { + + companion object { + + /** Returns a mutable builder for constructing an instance of [GridClient]. */ + fun builder() = Builder() + + /** + * Returns a client configured using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + fun fromEnv(): GridClient = builder().fromEnv().build() + } + + /** A builder for [GridOkHttpClient]. */ + class Builder internal constructor() { + + private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var dispatcherExecutorService: ExecutorService? = null + private var proxy: Proxy? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null + + /** + * The executor service to use for running HTTP requests. + * + * Defaults to OkHttp's + * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104). + * + * This class takes ownership of the executor service and shuts it down when closed. + */ + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + + /** + * The socket factory used to secure HTTPS connections. + * + * If this is set, then [trustManager] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + /** + * The trust manager used to secure HTTPS connections. + * + * If this is set, then [sslSocketFactory] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + /** + * The verifier used to confirm that response certificates apply to requested hostnames for + * HTTPS connections. + * + * If unset, then a default hostname verifier is used. + */ + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + clientOptions.checkJacksonVersionCompatibility(checkJacksonVersionCompatibility) + } + + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.grid.api.core.jsonMapper]. The default is usually sufficient and rarely + * needs to be overridden. + */ + fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) } + + /** + * The interface to use for delaying execution, like during retries. + * + * This is primarily useful for using fake delays in tests. + * + * Defaults to real execution delays. + * + * This class takes ownership of the sleeper and closes it when closed. + */ + fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) } + + /** + * The clock to use for operations that require timing, like retries. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.lightspark.com/grid/2025-10-13`. + */ + fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) } + + /** + * Whether to call `validate` on every response before returning it. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) } + + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + /** API token authentication using format `:` */ + fun username(username: String) = apply { clientOptions.username(username) } + + /** API token authentication using format `:` */ + fun password(password: String) = apply { clientOptions.password(password) } + + /** + * Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to + * verify that the webhook was sent by Grid. + * + * To verify the signature: + * 1. Get the Grid public key provided to you during integration + * 2. Decode the base64 signature from the header + * 3. Create a SHA-256 hash of the request body + * 4. Verify the signature using the public key and the hash + * + * If the signature verification succeeds, the webhook is authentic. If not, it should be + * rejected. + */ + fun webhookSignature(webhookSignature: String?) = apply { + clientOptions.webhookSignature(webhookSignature) + } + + fun headers(headers: Headers) = apply { clientOptions.headers(headers) } + + fun headers(headers: Map>) = apply { + clientOptions.headers(headers) + } + + fun putHeader(name: String, value: String) = apply { clientOptions.putHeader(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { + clientOptions.putHeaders(name, values) + } + + fun putAllHeaders(headers: Headers) = apply { clientOptions.putAllHeaders(headers) } + + fun putAllHeaders(headers: Map>) = apply { + clientOptions.putAllHeaders(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { + clientOptions.replaceHeaders(name, value) + } + + fun replaceHeaders(name: String, values: Iterable) = apply { + clientOptions.replaceHeaders(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { clientOptions.replaceAllHeaders(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + clientOptions.replaceAllHeaders(headers) + } + + fun removeHeaders(name: String) = apply { clientOptions.removeHeaders(name) } + + fun removeAllHeaders(names: Set) = apply { clientOptions.removeAllHeaders(names) } + + fun queryParams(queryParams: QueryParams) = apply { clientOptions.queryParams(queryParams) } + + fun queryParams(queryParams: Map>) = apply { + clientOptions.queryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { + clientOptions.putQueryParam(key, value) + } + + fun putQueryParams(key: String, values: Iterable) = apply { + clientOptions.putQueryParams(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + clientOptions.replaceQueryParams(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + clientOptions.replaceQueryParams(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun removeQueryParams(key: String) = apply { clientOptions.removeQueryParams(key) } + + fun removeAllQueryParams(keys: Set) = apply { + clientOptions.removeAllQueryParams(keys) + } + + /** + * Updates configuration using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + fun fromEnv() = apply { clientOptions.fromEnv() } + + /** + * Returns an immutable instance of [GridClient]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): GridClient = + GridClientImpl( + clientOptions + .httpClient( + OkHttpClient.builder() + .timeout(clientOptions.timeout()) + .proxy(proxy) + .dispatcherExecutorService(dispatcherExecutorService) + .sslSocketFactory(sslSocketFactory) + .trustManager(trustManager) + .hostnameVerifier(hostnameVerifier) + .build() + ) + .build() + ) + } +} diff --git a/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt b/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt new file mode 100644 index 00000000..9ccf40b6 --- /dev/null +++ b/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/GridOkHttpClientAsync.kt @@ -0,0 +1,323 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.client.okhttp + +import com.fasterxml.jackson.databind.json.JsonMapper +import com.grid.api.client.GridClientAsync +import com.grid.api.client.GridClientAsyncImpl +import com.grid.api.core.ClientOptions +import com.grid.api.core.Sleeper +import com.grid.api.core.Timeout +import com.grid.api.core.http.Headers +import com.grid.api.core.http.HttpClient +import com.grid.api.core.http.QueryParams +import com.grid.api.core.jsonMapper +import java.net.Proxy +import java.time.Clock +import java.time.Duration +import java.util.concurrent.ExecutorService +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager + +/** + * A class that allows building an instance of [GridClientAsync] with [OkHttpClient] as the + * underlying [HttpClient]. + */ +class GridOkHttpClientAsync private constructor() { + + companion object { + + /** Returns a mutable builder for constructing an instance of [GridClientAsync]. */ + fun builder() = Builder() + + /** + * Returns a client configured using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + fun fromEnv(): GridClientAsync = builder().fromEnv().build() + } + + /** A builder for [GridOkHttpClientAsync]. */ + class Builder internal constructor() { + + private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var dispatcherExecutorService: ExecutorService? = null + private var proxy: Proxy? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null + + /** + * The executor service to use for running HTTP requests. + * + * Defaults to OkHttp's + * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104). + * + * This class takes ownership of the executor service and shuts it down when closed. + */ + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + + /** + * The socket factory used to secure HTTPS connections. + * + * If this is set, then [trustManager] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + /** + * The trust manager used to secure HTTPS connections. + * + * If this is set, then [sslSocketFactory] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + /** + * The verifier used to confirm that response certificates apply to requested hostnames for + * HTTPS connections. + * + * If unset, then a default hostname verifier is used. + */ + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + clientOptions.checkJacksonVersionCompatibility(checkJacksonVersionCompatibility) + } + + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.grid.api.core.jsonMapper]. The default is usually sufficient and rarely + * needs to be overridden. + */ + fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) } + + /** + * The interface to use for delaying execution, like during retries. + * + * This is primarily useful for using fake delays in tests. + * + * Defaults to real execution delays. + * + * This class takes ownership of the sleeper and closes it when closed. + */ + fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) } + + /** + * The clock to use for operations that require timing, like retries. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.lightspark.com/grid/2025-10-13`. + */ + fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) } + + /** + * Whether to call `validate` on every response before returning it. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) } + + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + /** API token authentication using format `:` */ + fun username(username: String) = apply { clientOptions.username(username) } + + /** API token authentication using format `:` */ + fun password(password: String) = apply { clientOptions.password(password) } + + /** + * Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to + * verify that the webhook was sent by Grid. + * + * To verify the signature: + * 1. Get the Grid public key provided to you during integration + * 2. Decode the base64 signature from the header + * 3. Create a SHA-256 hash of the request body + * 4. Verify the signature using the public key and the hash + * + * If the signature verification succeeds, the webhook is authentic. If not, it should be + * rejected. + */ + fun webhookSignature(webhookSignature: String?) = apply { + clientOptions.webhookSignature(webhookSignature) + } + + fun headers(headers: Headers) = apply { clientOptions.headers(headers) } + + fun headers(headers: Map>) = apply { + clientOptions.headers(headers) + } + + fun putHeader(name: String, value: String) = apply { clientOptions.putHeader(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { + clientOptions.putHeaders(name, values) + } + + fun putAllHeaders(headers: Headers) = apply { clientOptions.putAllHeaders(headers) } + + fun putAllHeaders(headers: Map>) = apply { + clientOptions.putAllHeaders(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { + clientOptions.replaceHeaders(name, value) + } + + fun replaceHeaders(name: String, values: Iterable) = apply { + clientOptions.replaceHeaders(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { clientOptions.replaceAllHeaders(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + clientOptions.replaceAllHeaders(headers) + } + + fun removeHeaders(name: String) = apply { clientOptions.removeHeaders(name) } + + fun removeAllHeaders(names: Set) = apply { clientOptions.removeAllHeaders(names) } + + fun queryParams(queryParams: QueryParams) = apply { clientOptions.queryParams(queryParams) } + + fun queryParams(queryParams: Map>) = apply { + clientOptions.queryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { + clientOptions.putQueryParam(key, value) + } + + fun putQueryParams(key: String, values: Iterable) = apply { + clientOptions.putQueryParams(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + clientOptions.replaceQueryParams(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + clientOptions.replaceQueryParams(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun removeQueryParams(key: String) = apply { clientOptions.removeQueryParams(key) } + + fun removeAllQueryParams(keys: Set) = apply { + clientOptions.removeAllQueryParams(keys) + } + + /** + * Updates configuration using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + fun fromEnv() = apply { clientOptions.fromEnv() } + + /** + * Returns an immutable instance of [GridClientAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): GridClientAsync = + GridClientAsyncImpl( + clientOptions + .httpClient( + OkHttpClient.builder() + .timeout(clientOptions.timeout()) + .proxy(proxy) + .dispatcherExecutorService(dispatcherExecutorService) + .sslSocketFactory(sslSocketFactory) + .trustManager(trustManager) + .hostnameVerifier(hostnameVerifier) + .build() + ) + .build() + ) + } +} diff --git a/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/OkHttpClient.kt b/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/OkHttpClient.kt new file mode 100644 index 00000000..b8c463a3 --- /dev/null +++ b/grid-kotlin-client-okhttp/src/main/kotlin/com/grid/api/client/okhttp/OkHttpClient.kt @@ -0,0 +1,268 @@ +package com.grid.api.client.okhttp + +import com.grid.api.core.RequestOptions +import com.grid.api.core.Timeout +import com.grid.api.core.http.Headers +import com.grid.api.core.http.HttpClient +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpRequestBody +import com.grid.api.core.http.HttpResponse +import com.grid.api.errors.GridIoException +import java.io.IOException +import java.io.InputStream +import java.net.Proxy +import java.time.Duration +import java.util.concurrent.ExecutorService +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import kotlinx.coroutines.suspendCancellableCoroutine +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Dispatcher +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.Request +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import okhttp3.logging.HttpLoggingInterceptor +import okio.BufferedSink + +class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpClient) : + HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + val call = newCall(request, requestOptions) + + return try { + call.execute().toResponse() + } catch (e: IOException) { + throw GridIoException("Request failed", e) + } finally { + request.body?.close() + } + } + + override suspend fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + val call = newCall(request, requestOptions) + + return try { + call.executeAsync().toResponse() + } catch (e: IOException) { + throw GridIoException("Request failed", e) + } finally { + request.body?.close() + } + } + + override fun close() { + okHttpClient.dispatcher.executorService.shutdown() + okHttpClient.connectionPool.evictAll() + okHttpClient.cache?.close() + } + + private fun newCall(request: HttpRequest, requestOptions: RequestOptions): Call { + val clientBuilder = okHttpClient.newBuilder() + + val logLevel = + when (System.getenv("GRID_LOG")?.lowercase()) { + "info" -> HttpLoggingInterceptor.Level.BASIC + "debug" -> HttpLoggingInterceptor.Level.BODY + else -> null + } + if (logLevel != null) { + clientBuilder.addNetworkInterceptor( + HttpLoggingInterceptor().setLevel(logLevel).apply { + redactHeader("Authorization") + redactHeader("X-Grid-Signature") + } + ) + } + + requestOptions.timeout?.let { + clientBuilder + .connectTimeout(it.connect()) + .readTimeout(it.read()) + .writeTimeout(it.write()) + .callTimeout(it.request()) + } + + val client = clientBuilder.build() + return client.newCall(request.toRequest(client)) + } + + private suspend fun Call.executeAsync(): Response = + suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { this.cancel() } + + enqueue( + object : Callback { + override fun onFailure(call: Call, e: IOException) { + continuation.resumeWith(Result.failure(e)) + } + + override fun onResponse(call: Call, response: Response) { + continuation.resumeWith(Result.success(response)) + } + } + ) + } + + private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient): Request { + var body: RequestBody? = body?.toRequestBody() + if (body == null && requiresBody(method)) { + body = "".toRequestBody() + } + + val builder = Request.Builder().url(toUrl()).method(method.name, body) + headers.names().forEach { name -> + headers.values(name).forEach { builder.addHeader(name, it) } + } + + if ( + !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0 + ) { + builder.addHeader( + "X-Stainless-Read-Timeout", + Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(), + ) + } + if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) { + builder.addHeader( + "X-Stainless-Timeout", + Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(), + ) + } + + return builder.build() + } + + /** `OkHttpClient` always requires a request body for some methods. */ + private fun requiresBody(method: HttpMethod): Boolean = + when (method) { + HttpMethod.POST, + HttpMethod.PUT, + HttpMethod.PATCH -> true + else -> false + } + + private fun HttpRequest.toUrl(): String { + val builder = baseUrl.toHttpUrl().newBuilder() + pathSegments.forEach(builder::addPathSegment) + queryParams.keys().forEach { key -> + queryParams.values(key).forEach { builder.addQueryParameter(key, it) } + } + + return builder.toString() + } + + private fun HttpRequestBody.toRequestBody(): RequestBody { + val mediaType = contentType()?.toMediaType() + val length = contentLength() + + return object : RequestBody() { + override fun contentType(): MediaType? = mediaType + + override fun contentLength(): Long = length + + override fun isOneShot(): Boolean = !repeatable() + + override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream()) + } + } + + private fun Response.toResponse(): HttpResponse { + val headers = headers.toHeaders() + + return object : HttpResponse { + override fun statusCode(): Int = code + + override fun headers(): Headers = headers + + override fun body(): InputStream = body!!.byteStream() + + override fun close() = body!!.close() + } + } + + private fun okhttp3.Headers.toHeaders(): Headers { + val headersBuilder = Headers.builder() + forEach { (name, value) -> headersBuilder.put(name, value) } + return headersBuilder.build() + } + + companion object { + fun builder() = Builder() + } + + class Builder internal constructor() { + + private var timeout: Timeout = Timeout.default() + private var proxy: Proxy? = null + private var dispatcherExecutorService: ExecutorService? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null + + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + fun build(): OkHttpClient = + OkHttpClient( + okhttp3.OkHttpClient.Builder() + // `RetryingHttpClient` handles retries if the user enabled them. + .retryOnConnectionFailure(false) + .connectTimeout(timeout.connect()) + .readTimeout(timeout.read()) + .writeTimeout(timeout.write()) + .callTimeout(timeout.request()) + .proxy(proxy) + .apply { + dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) } + + val sslSocketFactory = sslSocketFactory + val trustManager = trustManager + if (sslSocketFactory != null && trustManager != null) { + sslSocketFactory(sslSocketFactory, trustManager) + } else { + check((sslSocketFactory != null) == (trustManager != null)) { + "Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set" + } + } + + hostnameVerifier?.let(::hostnameVerifier) + } + .build() + .apply { + // We usually make all our requests to the same host so it makes sense to + // raise the per-host limit to the overall limit. + dispatcher.maxRequestsPerHost = dispatcher.maxRequests + } + ) + } +} diff --git a/grid-kotlin-core/build.gradle.kts b/grid-kotlin-core/build.gradle.kts new file mode 100644 index 00000000..472940f9 --- /dev/null +++ b/grid-kotlin-core/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("grid.kotlin") + id("grid.publish") +} + +configurations.all { + resolutionStrategy { + // Compile and test against a lower Jackson version to ensure we're compatible with it. Note that + // we generally support 2.13.4, but test against 2.14.0 because 2.13.4 has some annoying (but + // niche) bugs (users should upgrade if they encounter them). We publish with a higher version + // (see below) to ensure users depend on a secure version by default. + force("com.fasterxml.jackson.core:jackson-core:2.14.0") + force("com.fasterxml.jackson.core:jackson-databind:2.14.0") + force("com.fasterxml.jackson.core:jackson-annotations:2.14.0") + force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.0") + force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0") + force("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0") + } +} + +dependencies { + api("com.fasterxml.jackson.core:jackson-core:2.18.2") + api("com.fasterxml.jackson.core:jackson-databind:2.18.2") + api("com.google.errorprone:error_prone_annotations:2.33.0") + + implementation("com.fasterxml.jackson.core:jackson-annotations:2.18.2") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2") + implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") + + testImplementation(kotlin("test")) + testImplementation(project(":grid-kotlin-client-okhttp")) + testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2") + testImplementation("org.assertj:assertj-core:3.27.7") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3") + testImplementation("org.junit-pioneer:junit-pioneer:1.9.1") + testImplementation("org.mockito:mockito-core:5.14.2") + testImplementation("org.mockito:mockito-junit-jupiter:5.14.2") + testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClient.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClient.kt new file mode 100644 index 00000000..015370ab --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClient.kt @@ -0,0 +1,141 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.client + +import com.grid.api.core.ClientOptions +import com.grid.api.services.blocking.ConfigService +import com.grid.api.services.blocking.CustomerService +import com.grid.api.services.blocking.ExchangeRateService +import com.grid.api.services.blocking.InvitationService +import com.grid.api.services.blocking.PlaidService +import com.grid.api.services.blocking.PlatformService +import com.grid.api.services.blocking.QuoteService +import com.grid.api.services.blocking.ReceiverService +import com.grid.api.services.blocking.SandboxService +import com.grid.api.services.blocking.TokenService +import com.grid.api.services.blocking.TransactionService +import com.grid.api.services.blocking.TransferInService +import com.grid.api.services.blocking.TransferOutService +import com.grid.api.services.blocking.UmaProviderService +import com.grid.api.services.blocking.WebhookService + +/** + * A client for interacting with the Grid REST API synchronously. You can also switch to + * asynchronous execution via the [async] method. + * + * This client performs best when you create a single instance and reuse it for all interactions + * with the REST API. This is because each client holds its own connection pool and thread pools. + * Reusing connections and threads reduces latency and saves memory. The client also handles rate + * limiting per client. This means that creating and using multiple instances at the same time will + * not respect rate limits. + * + * The threads and connections that are held will be released automatically if they remain idle. But + * if you are writing an application that needs to aggressively release unused resources, then you + * may call [close]. + */ +interface GridClient { + + /** + * Returns a version of this client that uses asynchronous execution. + * + * The returned client shares its resources, like its connection pool and thread pools, with + * this client. + */ + fun async(): GridClientAsync + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): GridClient + + fun config(): ConfigService + + fun customers(): CustomerService + + fun platform(): PlatformService + + fun plaid(): PlaidService + + fun transferIn(): TransferInService + + fun transferOut(): TransferOutService + + fun receiver(): ReceiverService + + fun quotes(): QuoteService + + fun transactions(): TransactionService + + fun webhooks(): WebhookService + + fun invitations(): InvitationService + + fun sandbox(): SandboxService + + fun umaProviders(): UmaProviderService + + fun tokens(): TokenService + + fun exchangeRates(): ExchangeRateService + + /** + * Closes this client, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client is long-lived and + * usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default HTTP client + * automatically releases threads and connections if they remain idle, but if you are writing an + * application that needs to aggressively release unused resources, then you may call this + * method. + */ + fun close() + + /** A view of [GridClient] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): GridClient.WithRawResponse + + fun config(): ConfigService.WithRawResponse + + fun customers(): CustomerService.WithRawResponse + + fun platform(): PlatformService.WithRawResponse + + fun plaid(): PlaidService.WithRawResponse + + fun transferIn(): TransferInService.WithRawResponse + + fun transferOut(): TransferOutService.WithRawResponse + + fun receiver(): ReceiverService.WithRawResponse + + fun quotes(): QuoteService.WithRawResponse + + fun transactions(): TransactionService.WithRawResponse + + fun webhooks(): WebhookService.WithRawResponse + + fun invitations(): InvitationService.WithRawResponse + + fun sandbox(): SandboxService.WithRawResponse + + fun umaProviders(): UmaProviderService.WithRawResponse + + fun tokens(): TokenService.WithRawResponse + + fun exchangeRates(): ExchangeRateService.WithRawResponse + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsync.kt new file mode 100644 index 00000000..e1a2af3d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsync.kt @@ -0,0 +1,141 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.client + +import com.grid.api.core.ClientOptions +import com.grid.api.services.async.ConfigServiceAsync +import com.grid.api.services.async.CustomerServiceAsync +import com.grid.api.services.async.ExchangeRateServiceAsync +import com.grid.api.services.async.InvitationServiceAsync +import com.grid.api.services.async.PlaidServiceAsync +import com.grid.api.services.async.PlatformServiceAsync +import com.grid.api.services.async.QuoteServiceAsync +import com.grid.api.services.async.ReceiverServiceAsync +import com.grid.api.services.async.SandboxServiceAsync +import com.grid.api.services.async.TokenServiceAsync +import com.grid.api.services.async.TransactionServiceAsync +import com.grid.api.services.async.TransferInServiceAsync +import com.grid.api.services.async.TransferOutServiceAsync +import com.grid.api.services.async.UmaProviderServiceAsync +import com.grid.api.services.async.WebhookServiceAsync + +/** + * A client for interacting with the Grid REST API asynchronously. You can also switch to + * synchronous execution via the [sync] method. + * + * This client performs best when you create a single instance and reuse it for all interactions + * with the REST API. This is because each client holds its own connection pool and thread pools. + * Reusing connections and threads reduces latency and saves memory. The client also handles rate + * limiting per client. This means that creating and using multiple instances at the same time will + * not respect rate limits. + * + * The threads and connections that are held will be released automatically if they remain idle. But + * if you are writing an application that needs to aggressively release unused resources, then you + * may call [close]. + */ +interface GridClientAsync { + + /** + * Returns a version of this client that uses synchronous execution. + * + * The returned client shares its resources, like its connection pool and thread pools, with + * this client. + */ + fun sync(): GridClient + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): GridClientAsync + + fun config(): ConfigServiceAsync + + fun customers(): CustomerServiceAsync + + fun platform(): PlatformServiceAsync + + fun plaid(): PlaidServiceAsync + + fun transferIn(): TransferInServiceAsync + + fun transferOut(): TransferOutServiceAsync + + fun receiver(): ReceiverServiceAsync + + fun quotes(): QuoteServiceAsync + + fun transactions(): TransactionServiceAsync + + fun webhooks(): WebhookServiceAsync + + fun invitations(): InvitationServiceAsync + + fun sandbox(): SandboxServiceAsync + + fun umaProviders(): UmaProviderServiceAsync + + fun tokens(): TokenServiceAsync + + fun exchangeRates(): ExchangeRateServiceAsync + + /** + * Closes this client, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client is long-lived and + * usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default HTTP client + * automatically releases threads and connections if they remain idle, but if you are writing an + * application that needs to aggressively release unused resources, then you may call this + * method. + */ + fun close() + + /** A view of [GridClientAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): GridClientAsync.WithRawResponse + + fun config(): ConfigServiceAsync.WithRawResponse + + fun customers(): CustomerServiceAsync.WithRawResponse + + fun platform(): PlatformServiceAsync.WithRawResponse + + fun plaid(): PlaidServiceAsync.WithRawResponse + + fun transferIn(): TransferInServiceAsync.WithRawResponse + + fun transferOut(): TransferOutServiceAsync.WithRawResponse + + fun receiver(): ReceiverServiceAsync.WithRawResponse + + fun quotes(): QuoteServiceAsync.WithRawResponse + + fun transactions(): TransactionServiceAsync.WithRawResponse + + fun webhooks(): WebhookServiceAsync.WithRawResponse + + fun invitations(): InvitationServiceAsync.WithRawResponse + + fun sandbox(): SandboxServiceAsync.WithRawResponse + + fun umaProviders(): UmaProviderServiceAsync.WithRawResponse + + fun tokens(): TokenServiceAsync.WithRawResponse + + fun exchangeRates(): ExchangeRateServiceAsync.WithRawResponse + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt new file mode 100644 index 00000000..ab47b95a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientAsyncImpl.kt @@ -0,0 +1,254 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.client + +import com.grid.api.core.ClientOptions +import com.grid.api.core.getPackageVersion +import com.grid.api.services.async.ConfigServiceAsync +import com.grid.api.services.async.ConfigServiceAsyncImpl +import com.grid.api.services.async.CustomerServiceAsync +import com.grid.api.services.async.CustomerServiceAsyncImpl +import com.grid.api.services.async.ExchangeRateServiceAsync +import com.grid.api.services.async.ExchangeRateServiceAsyncImpl +import com.grid.api.services.async.InvitationServiceAsync +import com.grid.api.services.async.InvitationServiceAsyncImpl +import com.grid.api.services.async.PlaidServiceAsync +import com.grid.api.services.async.PlaidServiceAsyncImpl +import com.grid.api.services.async.PlatformServiceAsync +import com.grid.api.services.async.PlatformServiceAsyncImpl +import com.grid.api.services.async.QuoteServiceAsync +import com.grid.api.services.async.QuoteServiceAsyncImpl +import com.grid.api.services.async.ReceiverServiceAsync +import com.grid.api.services.async.ReceiverServiceAsyncImpl +import com.grid.api.services.async.SandboxServiceAsync +import com.grid.api.services.async.SandboxServiceAsyncImpl +import com.grid.api.services.async.TokenServiceAsync +import com.grid.api.services.async.TokenServiceAsyncImpl +import com.grid.api.services.async.TransactionServiceAsync +import com.grid.api.services.async.TransactionServiceAsyncImpl +import com.grid.api.services.async.TransferInServiceAsync +import com.grid.api.services.async.TransferInServiceAsyncImpl +import com.grid.api.services.async.TransferOutServiceAsync +import com.grid.api.services.async.TransferOutServiceAsyncImpl +import com.grid.api.services.async.UmaProviderServiceAsync +import com.grid.api.services.async.UmaProviderServiceAsyncImpl +import com.grid.api.services.async.WebhookServiceAsync +import com.grid.api.services.async.WebhookServiceAsyncImpl + +class GridClientAsyncImpl(private val clientOptions: ClientOptions) : GridClientAsync { + + private val clientOptionsWithUserAgent = + if (clientOptions.headers.names().contains("User-Agent")) clientOptions + else + clientOptions + .toBuilder() + .putHeader("User-Agent", "${javaClass.simpleName}/Kotlin ${getPackageVersion()}") + .build() + + // Pass the original clientOptions so that this client sets its own User-Agent. + private val sync: GridClient by lazy { GridClientImpl(clientOptions) } + + private val withRawResponse: GridClientAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val config: ConfigServiceAsync by lazy { + ConfigServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val customers: CustomerServiceAsync by lazy { + CustomerServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val platform: PlatformServiceAsync by lazy { + PlatformServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val plaid: PlaidServiceAsync by lazy { + PlaidServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val transferIn: TransferInServiceAsync by lazy { + TransferInServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val transferOut: TransferOutServiceAsync by lazy { + TransferOutServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val receiver: ReceiverServiceAsync by lazy { + ReceiverServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val quotes: QuoteServiceAsync by lazy { + QuoteServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val transactions: TransactionServiceAsync by lazy { + TransactionServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val webhooks: WebhookServiceAsync by lazy { + WebhookServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val invitations: InvitationServiceAsync by lazy { + InvitationServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val sandbox: SandboxServiceAsync by lazy { + SandboxServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val umaProviders: UmaProviderServiceAsync by lazy { + UmaProviderServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val tokens: TokenServiceAsync by lazy { + TokenServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val exchangeRates: ExchangeRateServiceAsync by lazy { + ExchangeRateServiceAsyncImpl(clientOptionsWithUserAgent) + } + + override fun sync(): GridClient = sync + + override fun withRawResponse(): GridClientAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): GridClientAsync = + GridClientAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun config(): ConfigServiceAsync = config + + override fun customers(): CustomerServiceAsync = customers + + override fun platform(): PlatformServiceAsync = platform + + override fun plaid(): PlaidServiceAsync = plaid + + override fun transferIn(): TransferInServiceAsync = transferIn + + override fun transferOut(): TransferOutServiceAsync = transferOut + + override fun receiver(): ReceiverServiceAsync = receiver + + override fun quotes(): QuoteServiceAsync = quotes + + override fun transactions(): TransactionServiceAsync = transactions + + override fun webhooks(): WebhookServiceAsync = webhooks + + override fun invitations(): InvitationServiceAsync = invitations + + override fun sandbox(): SandboxServiceAsync = sandbox + + override fun umaProviders(): UmaProviderServiceAsync = umaProviders + + override fun tokens(): TokenServiceAsync = tokens + + override fun exchangeRates(): ExchangeRateServiceAsync = exchangeRates + + override fun close() = clientOptions.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GridClientAsync.WithRawResponse { + + private val config: ConfigServiceAsync.WithRawResponse by lazy { + ConfigServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val customers: CustomerServiceAsync.WithRawResponse by lazy { + CustomerServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val platform: PlatformServiceAsync.WithRawResponse by lazy { + PlatformServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val plaid: PlaidServiceAsync.WithRawResponse by lazy { + PlaidServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val transferIn: TransferInServiceAsync.WithRawResponse by lazy { + TransferInServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val transferOut: TransferOutServiceAsync.WithRawResponse by lazy { + TransferOutServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val receiver: ReceiverServiceAsync.WithRawResponse by lazy { + ReceiverServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val quotes: QuoteServiceAsync.WithRawResponse by lazy { + QuoteServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val transactions: TransactionServiceAsync.WithRawResponse by lazy { + TransactionServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val webhooks: WebhookServiceAsync.WithRawResponse by lazy { + WebhookServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val invitations: InvitationServiceAsync.WithRawResponse by lazy { + InvitationServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val sandbox: SandboxServiceAsync.WithRawResponse by lazy { + SandboxServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val umaProviders: UmaProviderServiceAsync.WithRawResponse by lazy { + UmaProviderServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val tokens: TokenServiceAsync.WithRawResponse by lazy { + TokenServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val exchangeRates: ExchangeRateServiceAsync.WithRawResponse by lazy { + ExchangeRateServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): GridClientAsync.WithRawResponse = + GridClientAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun config(): ConfigServiceAsync.WithRawResponse = config + + override fun customers(): CustomerServiceAsync.WithRawResponse = customers + + override fun platform(): PlatformServiceAsync.WithRawResponse = platform + + override fun plaid(): PlaidServiceAsync.WithRawResponse = plaid + + override fun transferIn(): TransferInServiceAsync.WithRawResponse = transferIn + + override fun transferOut(): TransferOutServiceAsync.WithRawResponse = transferOut + + override fun receiver(): ReceiverServiceAsync.WithRawResponse = receiver + + override fun quotes(): QuoteServiceAsync.WithRawResponse = quotes + + override fun transactions(): TransactionServiceAsync.WithRawResponse = transactions + + override fun webhooks(): WebhookServiceAsync.WithRawResponse = webhooks + + override fun invitations(): InvitationServiceAsync.WithRawResponse = invitations + + override fun sandbox(): SandboxServiceAsync.WithRawResponse = sandbox + + override fun umaProviders(): UmaProviderServiceAsync.WithRawResponse = umaProviders + + override fun tokens(): TokenServiceAsync.WithRawResponse = tokens + + override fun exchangeRates(): ExchangeRateServiceAsync.WithRawResponse = exchangeRates + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt new file mode 100644 index 00000000..8deae54f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/client/GridClientImpl.kt @@ -0,0 +1,240 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.client + +import com.grid.api.core.ClientOptions +import com.grid.api.core.getPackageVersion +import com.grid.api.services.blocking.ConfigService +import com.grid.api.services.blocking.ConfigServiceImpl +import com.grid.api.services.blocking.CustomerService +import com.grid.api.services.blocking.CustomerServiceImpl +import com.grid.api.services.blocking.ExchangeRateService +import com.grid.api.services.blocking.ExchangeRateServiceImpl +import com.grid.api.services.blocking.InvitationService +import com.grid.api.services.blocking.InvitationServiceImpl +import com.grid.api.services.blocking.PlaidService +import com.grid.api.services.blocking.PlaidServiceImpl +import com.grid.api.services.blocking.PlatformService +import com.grid.api.services.blocking.PlatformServiceImpl +import com.grid.api.services.blocking.QuoteService +import com.grid.api.services.blocking.QuoteServiceImpl +import com.grid.api.services.blocking.ReceiverService +import com.grid.api.services.blocking.ReceiverServiceImpl +import com.grid.api.services.blocking.SandboxService +import com.grid.api.services.blocking.SandboxServiceImpl +import com.grid.api.services.blocking.TokenService +import com.grid.api.services.blocking.TokenServiceImpl +import com.grid.api.services.blocking.TransactionService +import com.grid.api.services.blocking.TransactionServiceImpl +import com.grid.api.services.blocking.TransferInService +import com.grid.api.services.blocking.TransferInServiceImpl +import com.grid.api.services.blocking.TransferOutService +import com.grid.api.services.blocking.TransferOutServiceImpl +import com.grid.api.services.blocking.UmaProviderService +import com.grid.api.services.blocking.UmaProviderServiceImpl +import com.grid.api.services.blocking.WebhookService +import com.grid.api.services.blocking.WebhookServiceImpl + +class GridClientImpl(private val clientOptions: ClientOptions) : GridClient { + + private val clientOptionsWithUserAgent = + if (clientOptions.headers.names().contains("User-Agent")) clientOptions + else + clientOptions + .toBuilder() + .putHeader("User-Agent", "${javaClass.simpleName}/Kotlin ${getPackageVersion()}") + .build() + + // Pass the original clientOptions so that this client sets its own User-Agent. + private val async: GridClientAsync by lazy { GridClientAsyncImpl(clientOptions) } + + private val withRawResponse: GridClient.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val config: ConfigService by lazy { ConfigServiceImpl(clientOptionsWithUserAgent) } + + private val customers: CustomerService by lazy { + CustomerServiceImpl(clientOptionsWithUserAgent) + } + + private val platform: PlatformService by lazy { + PlatformServiceImpl(clientOptionsWithUserAgent) + } + + private val plaid: PlaidService by lazy { PlaidServiceImpl(clientOptionsWithUserAgent) } + + private val transferIn: TransferInService by lazy { + TransferInServiceImpl(clientOptionsWithUserAgent) + } + + private val transferOut: TransferOutService by lazy { + TransferOutServiceImpl(clientOptionsWithUserAgent) + } + + private val receiver: ReceiverService by lazy { + ReceiverServiceImpl(clientOptionsWithUserAgent) + } + + private val quotes: QuoteService by lazy { QuoteServiceImpl(clientOptionsWithUserAgent) } + + private val transactions: TransactionService by lazy { + TransactionServiceImpl(clientOptionsWithUserAgent) + } + + private val webhooks: WebhookService by lazy { WebhookServiceImpl(clientOptionsWithUserAgent) } + + private val invitations: InvitationService by lazy { + InvitationServiceImpl(clientOptionsWithUserAgent) + } + + private val sandbox: SandboxService by lazy { SandboxServiceImpl(clientOptionsWithUserAgent) } + + private val umaProviders: UmaProviderService by lazy { + UmaProviderServiceImpl(clientOptionsWithUserAgent) + } + + private val tokens: TokenService by lazy { TokenServiceImpl(clientOptionsWithUserAgent) } + + private val exchangeRates: ExchangeRateService by lazy { + ExchangeRateServiceImpl(clientOptionsWithUserAgent) + } + + override fun async(): GridClientAsync = async + + override fun withRawResponse(): GridClient.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): GridClient = + GridClientImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun config(): ConfigService = config + + override fun customers(): CustomerService = customers + + override fun platform(): PlatformService = platform + + override fun plaid(): PlaidService = plaid + + override fun transferIn(): TransferInService = transferIn + + override fun transferOut(): TransferOutService = transferOut + + override fun receiver(): ReceiverService = receiver + + override fun quotes(): QuoteService = quotes + + override fun transactions(): TransactionService = transactions + + override fun webhooks(): WebhookService = webhooks + + override fun invitations(): InvitationService = invitations + + override fun sandbox(): SandboxService = sandbox + + override fun umaProviders(): UmaProviderService = umaProviders + + override fun tokens(): TokenService = tokens + + override fun exchangeRates(): ExchangeRateService = exchangeRates + + override fun close() = clientOptions.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + GridClient.WithRawResponse { + + private val config: ConfigService.WithRawResponse by lazy { + ConfigServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val customers: CustomerService.WithRawResponse by lazy { + CustomerServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val platform: PlatformService.WithRawResponse by lazy { + PlatformServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val plaid: PlaidService.WithRawResponse by lazy { + PlaidServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val transferIn: TransferInService.WithRawResponse by lazy { + TransferInServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val transferOut: TransferOutService.WithRawResponse by lazy { + TransferOutServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val receiver: ReceiverService.WithRawResponse by lazy { + ReceiverServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val quotes: QuoteService.WithRawResponse by lazy { + QuoteServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val transactions: TransactionService.WithRawResponse by lazy { + TransactionServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val webhooks: WebhookService.WithRawResponse by lazy { + WebhookServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val invitations: InvitationService.WithRawResponse by lazy { + InvitationServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val sandbox: SandboxService.WithRawResponse by lazy { + SandboxServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val umaProviders: UmaProviderService.WithRawResponse by lazy { + UmaProviderServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val tokens: TokenService.WithRawResponse by lazy { + TokenServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val exchangeRates: ExchangeRateService.WithRawResponse by lazy { + ExchangeRateServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): GridClient.WithRawResponse = + GridClientImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun config(): ConfigService.WithRawResponse = config + + override fun customers(): CustomerService.WithRawResponse = customers + + override fun platform(): PlatformService.WithRawResponse = platform + + override fun plaid(): PlaidService.WithRawResponse = plaid + + override fun transferIn(): TransferInService.WithRawResponse = transferIn + + override fun transferOut(): TransferOutService.WithRawResponse = transferOut + + override fun receiver(): ReceiverService.WithRawResponse = receiver + + override fun quotes(): QuoteService.WithRawResponse = quotes + + override fun transactions(): TransactionService.WithRawResponse = transactions + + override fun webhooks(): WebhookService.WithRawResponse = webhooks + + override fun invitations(): InvitationService.WithRawResponse = invitations + + override fun sandbox(): SandboxService.WithRawResponse = sandbox + + override fun umaProviders(): UmaProviderService.WithRawResponse = umaProviders + + override fun tokens(): TokenService.WithRawResponse = tokens + + override fun exchangeRates(): ExchangeRateService.WithRawResponse = exchangeRates + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPager.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPager.kt new file mode 100644 index 00000000..b0fc542b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPager.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +class AutoPager private constructor(private val firstPage: Page) : Sequence { + + companion object { + + fun from(firstPage: Page): AutoPager = AutoPager(firstPage) + } + + override fun iterator(): Iterator = + generateSequence(firstPage) { if (it.hasNextPage()) it.nextPage() else null } + .flatMap { it.items() } + .iterator() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPagerAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPagerAsync.kt new file mode 100644 index 00000000..4fbbf942 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/AutoPagerAsync.kt @@ -0,0 +1,27 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.emitAll + +class AutoPagerAsync private constructor(private val firstPage: PageAsync) : Flow { + + companion object { + + fun from(firstPage: PageAsync): AutoPagerAsync = AutoPagerAsync(firstPage) + } + + override suspend fun collect(collector: FlowCollector) { + var page: PageAsync = firstPage + while (true) { + collector.emitAll(page.items().asFlow()) + if (!page.hasNextPage()) { + break + } + page = page.nextPage() + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseDeserializer.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseDeserializer.kt new file mode 100644 index 00000000..eb5ea2f6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseDeserializer.kt @@ -0,0 +1,44 @@ +package com.grid.api.core + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.BeanProperty +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.deser.ContextualDeserializer +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import kotlin.reflect.KClass + +abstract class BaseDeserializer(type: KClass) : + StdDeserializer(type.java), ContextualDeserializer { + + override fun createContextual( + context: DeserializationContext, + property: BeanProperty?, + ): JsonDeserializer { + return this + } + + override fun deserialize(parser: JsonParser, context: DeserializationContext): T { + return parser.codec.deserialize(parser.readValueAsTree()) + } + + protected abstract fun ObjectCodec.deserialize(node: JsonNode): T + + protected fun ObjectCodec.tryDeserialize(node: JsonNode, type: TypeReference): T? = + try { + readValue(treeAsTokens(node), type) + } catch (e: Exception) { + null + } + + protected fun ObjectCodec.tryDeserialize(node: JsonNode, type: JavaType): T? = + try { + readValue(treeAsTokens(node), type) + } catch (e: Exception) { + null + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseSerializer.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseSerializer.kt new file mode 100644 index 00000000..cb66115a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/BaseSerializer.kt @@ -0,0 +1,6 @@ +package com.grid.api.core + +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import kotlin.reflect.KClass + +abstract class BaseSerializer(type: KClass) : StdSerializer(type.java) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt new file mode 100644 index 00000000..131ab595 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt @@ -0,0 +1,86 @@ +package com.grid.api.core + +import com.fasterxml.jackson.core.Version +import com.fasterxml.jackson.core.util.VersionUtil + +fun checkRequired(name: String, condition: Boolean) = + check(condition) { "`$name` is required, but was not set" } + +fun checkRequired(name: String, value: T?): T = + checkNotNull(value) { "`$name` is required, but was not set" } + +internal fun checkKnown(name: String, value: JsonField): T = + value.asKnown() + ?: throw IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}") + +internal fun checkKnown(name: String, value: MultipartField): T = + value.value.asKnown() + ?: throw IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}") + +internal fun checkLength(name: String, value: String, length: Int): String = + value.also { + check(it.length == length) { "`$name` must have length $length, but was ${it.length}" } + } + +internal fun checkMinLength(name: String, value: String, minLength: Int): String = + value.also { + check(it.length >= minLength) { + if (minLength == 1) "`$name` must be non-empty, but was empty" + else "`$name` must have at least length $minLength, but was ${it.length}" + } + } + +internal fun checkMaxLength(name: String, value: String, maxLength: Int): String = + value.also { + check(it.length <= maxLength) { + "`$name` must have at most length $maxLength, but was ${it.length}" + } + } + +internal fun checkJacksonVersionCompatibility() { + val incompatibleJacksonVersions = + RUNTIME_JACKSON_VERSIONS.mapNotNull { + val badVersionReason = BAD_JACKSON_VERSIONS[it.toString()] + when { + it.majorVersion != MINIMUM_JACKSON_VERSION.majorVersion -> + it to "incompatible major version" + it.minorVersion < MINIMUM_JACKSON_VERSION.minorVersion -> + it to "minor version too low" + it.minorVersion == MINIMUM_JACKSON_VERSION.minorVersion && + it.patchLevel < MINIMUM_JACKSON_VERSION.patchLevel -> + it to "patch version too low" + badVersionReason != null -> it to badVersionReason + else -> null + } + } + check(incompatibleJacksonVersions.isEmpty()) { + """ +This SDK requires a minimum Jackson version of $MINIMUM_JACKSON_VERSION, but the following incompatible Jackson versions were detected at runtime: + +${incompatibleJacksonVersions.asSequence().map { (version, incompatibilityReason) -> + "- `${version.toFullString().replace("/", ":")}` ($incompatibilityReason)" +}.joinToString("\n")} + +This can happen if you are either: +1. Directly depending on different Jackson versions +2. Depending on some library that depends on different Jackson versions, potentially transitively + +Double-check that you are depending on compatible Jackson versions. + +See https://www.github.com/stainless-sdks/grid-kotlin#jackson for more information. + """ + .trimIndent() + } +} + +private val MINIMUM_JACKSON_VERSION: Version = VersionUtil.parseVersion("2.13.4", null, null) +private val BAD_JACKSON_VERSIONS: Map = + mapOf("2.18.1" to "due to https://github.com/FasterXML/jackson-databind/issues/4639") +private val RUNTIME_JACKSON_VERSIONS: List = + listOf( + com.fasterxml.jackson.core.json.PackageVersion.VERSION, + com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION, + com.fasterxml.jackson.datatype.jdk8.PackageVersion.VERSION, + com.fasterxml.jackson.datatype.jsr310.PackageVersion.VERSION, + com.fasterxml.jackson.module.kotlin.PackageVersion.VERSION, + ) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/ClientOptions.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/ClientOptions.kt new file mode 100644 index 00000000..e8dee2e0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/ClientOptions.kt @@ -0,0 +1,510 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +import com.fasterxml.jackson.databind.json.JsonMapper +import com.grid.api.core.http.Headers +import com.grid.api.core.http.HttpClient +import com.grid.api.core.http.PhantomReachableClosingHttpClient +import com.grid.api.core.http.QueryParams +import com.grid.api.core.http.RetryingHttpClient +import java.time.Clock +import java.time.Duration +import java.util.Base64 + +/** A class representing the SDK client configuration. */ +class ClientOptions +private constructor( + private val originalHttpClient: HttpClient, + /** + * The HTTP client to use in the SDK. + * + * Use the one published in `grid-kotlin-client-okhttp` or implement your own. + * + * This class takes ownership of the client and closes it when closed. + */ + val httpClient: HttpClient, + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee that + * the SDK will work correctly when using an incompatible Jackson version. + */ + val checkJacksonVersionCompatibility: Boolean, + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.grid.api.core.jsonMapper]. The default is usually sufficient and rarely + * needs to be overridden. + */ + val jsonMapper: JsonMapper, + /** + * The interface to use for delaying execution, like during retries. + * + * This is primarily useful for using fake delays in tests. + * + * Defaults to real execution delays. + * + * This class takes ownership of the sleeper and closes it when closed. + */ + val sleeper: Sleeper, + /** + * The clock to use for operations that require timing, like retries. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + val clock: Clock, + private val baseUrl: String?, + /** Headers to send with the request. */ + val headers: Headers, + /** Query params to send with the request. */ + val queryParams: QueryParams, + /** + * Whether to call `validate` on every response before returning it. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + val responseValidation: Boolean, + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + val timeout: Timeout, + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + val maxRetries: Int, + /** API token authentication using format `:` */ + val username: String, + /** API token authentication using format `:` */ + val password: String, + /** + * Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to verify + * that the webhook was sent by Grid. + * + * To verify the signature: + * 1. Get the Grid public key provided to you during integration + * 2. Decode the base64 signature from the header + * 3. Create a SHA-256 hash of the request body + * 4. Verify the signature using the public key and the hash + * + * If the signature verification succeeds, the webhook is authentic. If not, it should be + * rejected. + */ + val webhookSignature: String?, +) { + + init { + if (checkJacksonVersionCompatibility) { + checkJacksonVersionCompatibility() + } + } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.lightspark.com/grid/2025-10-13`. + */ + fun baseUrl(): String = baseUrl ?: PRODUCTION_URL + + fun toBuilder() = Builder().from(this) + + companion object { + + const val PRODUCTION_URL = "https://api.lightspark.com/grid/2025-10-13" + + /** + * Returns a mutable builder for constructing an instance of [ClientOptions]. + * + * The following fields are required: + * ```kotlin + * .httpClient() + * .username() + * .password() + * ``` + */ + fun builder() = Builder() + + /** + * Returns options configured using system properties and environment variables. + * + * @see Builder.fromEnv + */ + fun fromEnv(): ClientOptions = builder().fromEnv().build() + } + + /** A builder for [ClientOptions]. */ + class Builder internal constructor() { + + private var httpClient: HttpClient? = null + private var checkJacksonVersionCompatibility: Boolean = true + private var jsonMapper: JsonMapper = jsonMapper() + private var sleeper: Sleeper? = null + private var clock: Clock = Clock.systemUTC() + private var baseUrl: String? = null + private var headers: Headers.Builder = Headers.builder() + private var queryParams: QueryParams.Builder = QueryParams.builder() + private var responseValidation: Boolean = false + private var timeout: Timeout = Timeout.default() + private var maxRetries: Int = 2 + private var username: String? = null + private var password: String? = null + private var webhookSignature: String? = null + + internal fun from(clientOptions: ClientOptions) = apply { + httpClient = clientOptions.originalHttpClient + checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility + jsonMapper = clientOptions.jsonMapper + sleeper = clientOptions.sleeper + clock = clientOptions.clock + baseUrl = clientOptions.baseUrl + headers = clientOptions.headers.toBuilder() + queryParams = clientOptions.queryParams.toBuilder() + responseValidation = clientOptions.responseValidation + timeout = clientOptions.timeout + maxRetries = clientOptions.maxRetries + username = clientOptions.username + password = clientOptions.password + webhookSignature = clientOptions.webhookSignature + } + + /** + * The HTTP client to use in the SDK. + * + * Use the one published in `grid-kotlin-client-okhttp` or implement your own. + * + * This class takes ownership of the client and closes it when closed. + */ + fun httpClient(httpClient: HttpClient) = apply { + this.httpClient = PhantomReachableClosingHttpClient(httpClient) + } + + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + this.checkJacksonVersionCompatibility = checkJacksonVersionCompatibility + } + + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.grid.api.core.jsonMapper]. The default is usually sufficient and rarely + * needs to be overridden. + */ + fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper } + + /** + * The interface to use for delaying execution, like during retries. + * + * This is primarily useful for using fake delays in tests. + * + * Defaults to real execution delays. + * + * This class takes ownership of the sleeper and closes it when closed. + */ + fun sleeper(sleeper: Sleeper) = apply { this.sleeper = PhantomReachableSleeper(sleeper) } + + /** + * The clock to use for operations that require timing, like retries. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { this.clock = clock } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.lightspark.com/grid/2025-10-13`. + */ + fun baseUrl(baseUrl: String?) = apply { this.baseUrl = baseUrl } + + /** + * Whether to call `validate` on every response before returning it. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + this.responseValidation = responseValidation + } + + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + + /** API token authentication using format `:` */ + fun username(username: String) = apply { this.username = username } + + /** API token authentication using format `:` */ + fun password(password: String) = apply { this.password = password } + + /** + * Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to + * verify that the webhook was sent by Grid. + * + * To verify the signature: + * 1. Get the Grid public key provided to you during integration + * 2. Decode the base64 signature from the header + * 3. Create a SHA-256 hash of the request body + * 4. Verify the signature using the public key and the hash + * + * If the signature verification succeeds, the webhook is authentic. If not, it should be + * rejected. + */ + fun webhookSignature(webhookSignature: String?) = apply { + this.webhookSignature = webhookSignature + } + + fun headers(headers: Headers) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun headers(headers: Map>) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun putHeader(name: String, value: String) = apply { headers.put(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { headers.put(name, values) } + + fun putAllHeaders(headers: Headers) = apply { this.headers.putAll(headers) } + + fun putAllHeaders(headers: Map>) = apply { + this.headers.putAll(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { headers.replace(name, value) } + + fun replaceHeaders(name: String, values: Iterable) = apply { + headers.replace(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { this.headers.replaceAll(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + this.headers.replaceAll(headers) + } + + fun removeHeaders(name: String) = apply { headers.remove(name) } + + fun removeAllHeaders(names: Set) = apply { headers.removeAll(names) } + + fun queryParams(queryParams: QueryParams) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun queryParams(queryParams: Map>) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { queryParams.put(key, value) } + + fun putQueryParams(key: String, values: Iterable) = apply { + queryParams.put(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.putAll(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + this.queryParams.putAll(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + queryParams.replace(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + queryParams.replace(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun removeQueryParams(key: String) = apply { queryParams.remove(key) } + + fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) } + + fun timeout(): Timeout = timeout + + /** + * Updates configuration using system properties and environment variables. + * + * See this table for the available options: + * + * |Setter |System property |Environment variable |Required|Default value | + * |------------------|-----------------------|------------------------|--------|----------------------------------------------| + * |`username` |`grid.username` |`GRID_USERNAME` |true |- | + * |`password` |`grid.password` |`GRID_PASSWORD` |true |- | + * |`webhookSignature`|`grid.webhookSignature`|`GRID_WEBHOOK_SIGNATURE`|false |- | + * |`baseUrl` |`grid.baseUrl` |`GRID_BASE_URL` |true |`"https://api.lightspark.com/grid/2025-10-13"`| + * + * System properties take precedence over environment variables. + */ + fun fromEnv() = apply { + (System.getProperty("grid.baseUrl") ?: System.getenv("GRID_BASE_URL"))?.let { + baseUrl(it) + } + (System.getProperty("grid.username") ?: System.getenv("GRID_USERNAME"))?.let { + username(it) + } + (System.getProperty("grid.password") ?: System.getenv("GRID_PASSWORD"))?.let { + password(it) + } + (System.getProperty("grid.webhookSignature") ?: System.getenv("GRID_WEBHOOK_SIGNATURE")) + ?.let { webhookSignature(it) } + } + + /** + * Returns an immutable instance of [ClientOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .httpClient() + * .username() + * .password() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ClientOptions { + val httpClient = checkRequired("httpClient", httpClient) + val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper()) + val username = checkRequired("username", username) + val password = checkRequired("password", password) + + val headers = Headers.builder() + val queryParams = QueryParams.builder() + headers.put("X-Stainless-Lang", "kotlin") + headers.put("X-Stainless-Arch", getOsArch()) + headers.put("X-Stainless-OS", getOsName()) + headers.put("X-Stainless-OS-Version", getOsVersion()) + headers.put("X-Stainless-Package-Version", getPackageVersion()) + headers.put("X-Stainless-Runtime", "JRE") + headers.put("X-Stainless-Runtime-Version", getJavaVersion()) + headers.put("X-Stainless-Kotlin-Version", KotlinVersion.CURRENT.toString()) + username.let { username -> + password.let { password -> + if (!username.isEmpty() && !password.isEmpty()) { + headers.put( + "Authorization", + "Basic ${Base64.getEncoder().encodeToString("$username:$password".toByteArray())}", + ) + } + } + } + webhookSignature?.let { + if (!it.isEmpty()) { + headers.put("X-Grid-Signature", it) + } + } + headers.replaceAll(this.headers.build()) + queryParams.replaceAll(this.queryParams.build()) + + return ClientOptions( + httpClient, + RetryingHttpClient.builder() + .httpClient(httpClient) + .sleeper(sleeper) + .clock(clock) + .maxRetries(maxRetries) + .build(), + checkJacksonVersionCompatibility, + jsonMapper, + sleeper, + clock, + baseUrl, + headers.build(), + queryParams.build(), + responseValidation, + timeout, + maxRetries, + username, + password, + webhookSignature, + ) + } + } + + /** + * Closes these client options, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client options are + * long-lived and usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default client automatically + * releases threads and connections if they remain idle, but if you are writing an application + * that needs to aggressively release unused resources, then you may call this method. + */ + fun close() { + httpClient.close() + sleeper.close() + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/DefaultSleeper.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/DefaultSleeper.kt new file mode 100644 index 00000000..89718946 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/DefaultSleeper.kt @@ -0,0 +1,14 @@ +package com.grid.api.core + +import java.time.Duration +import kotlin.time.toKotlinDuration +import kotlinx.coroutines.delay + +class DefaultSleeper : Sleeper { + + override fun sleep(duration: Duration) = Thread.sleep(duration.toMillis()) + + override suspend fun sleepAsync(duration: Duration) = delay(duration.toKotlinDuration()) + + override fun close() {} +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/ObjectMappers.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/ObjectMappers.kt new file mode 100644 index 00000000..e6c14c12 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/ObjectMappers.kt @@ -0,0 +1,178 @@ +@file:JvmName("ObjectMappers") + +package com.grid.api.core + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParseException +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.MapperFeature +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.cfg.CoercionAction +import com.fasterxml.jackson.databind.cfg.CoercionInputShape +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.type.LogicalType +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.kotlinModule +import java.io.InputStream +import java.time.DateTimeException +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.OffsetDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField + +fun jsonMapper(): JsonMapper = + JsonMapper.builder() + .addModule(kotlinModule()) + .addModule(Jdk8Module()) + .addModule(JavaTimeModule()) + .addModule( + SimpleModule() + .addSerializer(InputStreamSerializer) + .addDeserializer(OffsetDateTime::class.java, LenientOffsetDateTimeDeserializer()) + ) + .withCoercionConfig(LogicalType.Boolean) { + it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Integer) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Float) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Textual) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.DateTime) { + it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Array) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Collection) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Map) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.POJO) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + } + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .disable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS) + .disable(MapperFeature.AUTO_DETECT_CREATORS) + .disable(MapperFeature.AUTO_DETECT_FIELDS) + .disable(MapperFeature.AUTO_DETECT_GETTERS) + .disable(MapperFeature.AUTO_DETECT_IS_GETTERS) + .disable(MapperFeature.AUTO_DETECT_SETTERS) + .build() + +/** A serializer that serializes [InputStream] to bytes. */ +private object InputStreamSerializer : BaseSerializer(InputStream::class) { + + private fun readResolve(): Any = InputStreamSerializer + + override fun serialize( + value: InputStream?, + gen: JsonGenerator?, + serializers: SerializerProvider?, + ) { + if (value == null) { + gen?.writeNull() + } else { + value.use { gen?.writeBinary(it.readBytes()) } + } + } +} + +/** + * A deserializer that can deserialize [OffsetDateTime] from datetimes, dates, and zoned datetimes. + */ +private class LenientOffsetDateTimeDeserializer : + StdDeserializer(OffsetDateTime::class.java) { + + companion object { + + private val DATE_TIME_FORMATTERS = + listOf( + DateTimeFormatter.ISO_LOCAL_DATE_TIME, + DateTimeFormatter.ISO_LOCAL_DATE, + DateTimeFormatter.ISO_ZONED_DATE_TIME, + ) + } + + override fun logicalType(): LogicalType = LogicalType.DateTime + + override fun deserialize(p: JsonParser, context: DeserializationContext): OffsetDateTime { + val exceptions = mutableListOf() + + for (formatter in DATE_TIME_FORMATTERS) { + try { + val temporal = formatter.parse(p.text) + + return when { + !temporal.isSupported(ChronoField.HOUR_OF_DAY) -> + LocalDate.from(temporal) + .atStartOfDay() + .atZone(ZoneId.of("UTC")) + .toOffsetDateTime() + !temporal.isSupported(ChronoField.OFFSET_SECONDS) -> + LocalDateTime.from(temporal).atZone(ZoneId.of("UTC")).toOffsetDateTime() + else -> OffsetDateTime.from(temporal) + } + } catch (e: DateTimeException) { + exceptions.add(e) + } + } + + throw JsonParseException(p, "Cannot parse `OffsetDateTime` from value: ${p.text}").apply { + exceptions.forEach { addSuppressed(it) } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Page.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Page.kt new file mode 100644 index 00000000..333a78b8 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Page.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +/** + * An interface representing a single page, with items of type [T], from a paginated endpoint + * response. + * + * Implementations of this interface are expected to request additional pages synchronously. For + * asynchronous pagination, see the [PageAsync] interface. + */ +interface Page { + + /** + * Returns whether there's another page after this one. + * + * The method generally doesn't make requests so the result depends entirely on the data in this + * page. If a significant amount of time has passed between requesting this page and calling + * this method, then the result could be stale. + */ + fun hasNextPage(): Boolean + + /** + * Returns the page after this one by making another request. + * + * @throws IllegalStateException if it's impossible to get the next page. This exception is + * avoidable by calling [hasNextPage] first. + */ + fun nextPage(): Page + + /** Returns the items in this page. */ + fun items(): List +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PageAsync.kt new file mode 100644 index 00000000..c98558bd --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PageAsync.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +/** + * An interface representing a single page, with items of type [T], from a paginated endpoint + * response. + * + * Implementations of this interface are expected to request additional pages asynchronously. For + * synchronous pagination, see the [Page] interface. + */ +interface PageAsync { + + /** + * Returns whether there's another page after this one. + * + * The method generally doesn't make requests so the result depends entirely on the data in this + * page. If a significant amount of time has passed between requesting this page and calling + * this method, then the result could be stale. + */ + fun hasNextPage(): Boolean + + /** + * Returns the page after this one by making another request. + * + * @throws IllegalStateException if it's impossible to get the next page. This exception is + * avoidable by calling [hasNextPage] first. + */ + suspend fun nextPage(): PageAsync + + /** Returns the items in this page. */ + fun items(): List +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Params.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Params.kt new file mode 100644 index 00000000..60d99257 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Params.kt @@ -0,0 +1,16 @@ +package com.grid.api.core + +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams + +/** An interface representing parameters passed to a service method. */ +interface Params { + /** The full set of headers in the parameters, including both fixed and additional headers. */ + fun _headers(): Headers + + /** + * The full set of query params in the parameters, including both fixed and additional query + * params. + */ + fun _queryParams(): QueryParams +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachable.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachable.kt new file mode 100644 index 00000000..4bd55020 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachable.kt @@ -0,0 +1,54 @@ +@file:JvmName("PhantomReachable") + +package com.grid.api.core + +import com.grid.api.errors.GridException +import java.lang.reflect.InvocationTargetException + +/** + * Closes [closeable] when [observed] becomes only phantom reachable. + * + * This is a wrapper around a Java 9+ [java.lang.ref.Cleaner], or a no-op in older Java versions. + */ +internal fun closeWhenPhantomReachable(observed: Any, closeable: AutoCloseable) { + check(observed !== closeable) { + "`observed` cannot be the same object as `closeable` because it would never become phantom reachable" + } + closeWhenPhantomReachable(observed, closeable::close) +} + +/** + * Calls [close] when [observed] becomes only phantom reachable. + * + * This is a wrapper around a Java 9+ [java.lang.ref.Cleaner], or a no-op in older Java versions. + */ +internal fun closeWhenPhantomReachable(observed: Any, close: () -> Unit) { + closeWhenPhantomReachable?.let { it(observed, close) } +} + +private val closeWhenPhantomReachable: ((Any, () -> Unit) -> Unit)? by lazy { + try { + val cleanerClass = Class.forName("java.lang.ref.Cleaner") + val cleanerCreate = cleanerClass.getMethod("create") + val cleanerRegister = + cleanerClass.getMethod("register", Any::class.java, Runnable::class.java) + val cleanerObject = cleanerCreate.invoke(null); + + { observed, close -> + try { + cleanerRegister.invoke(cleanerObject, observed, Runnable { close() }) + } catch (e: ReflectiveOperationException) { + if (e is InvocationTargetException) { + when (val cause = e.cause) { + is RuntimeException, + is Error -> throw cause + } + } + throw GridException("Unexpected reflective invocation failure", e) + } + } + } catch (e: ReflectiveOperationException) { + // We're running Java 8, which has no Cleaner. + null + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableExecutorService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableExecutorService.kt new file mode 100644 index 00000000..0ce1d91f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableExecutorService.kt @@ -0,0 +1,58 @@ +package com.grid.api.core + +import java.util.concurrent.Callable +import java.util.concurrent.ExecutorService +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit + +/** + * A delegating wrapper around an [ExecutorService] that shuts it down once it's only phantom + * reachable. + * + * This class ensures the [ExecutorService] is shut down even if the user forgets to do it. + */ +internal class PhantomReachableExecutorService(private val executorService: ExecutorService) : + ExecutorService { + init { + closeWhenPhantomReachable(this) { executorService.shutdown() } + } + + override fun execute(command: Runnable) = executorService.execute(command) + + override fun shutdown() = executorService.shutdown() + + override fun shutdownNow(): MutableList = executorService.shutdownNow() + + override fun isShutdown(): Boolean = executorService.isShutdown + + override fun isTerminated(): Boolean = executorService.isTerminated + + override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean = + executorService.awaitTermination(timeout, unit) + + override fun submit(task: Callable): Future = executorService.submit(task) + + override fun submit(task: Runnable, result: T): Future = + executorService.submit(task, result) + + override fun submit(task: Runnable): Future<*> = executorService.submit(task) + + override fun invokeAll( + tasks: MutableCollection> + ): MutableList> = executorService.invokeAll(tasks) + + override fun invokeAll( + tasks: MutableCollection>, + timeout: Long, + unit: TimeUnit, + ): MutableList> = executorService.invokeAll(tasks, timeout, unit) + + override fun invokeAny(tasks: MutableCollection>): T = + executorService.invokeAny(tasks) + + override fun invokeAny( + tasks: MutableCollection>, + timeout: Long, + unit: TimeUnit, + ): T = executorService.invokeAny(tasks, timeout, unit) +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableSleeper.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableSleeper.kt new file mode 100644 index 00000000..c94e5e07 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PhantomReachableSleeper.kt @@ -0,0 +1,21 @@ +package com.grid.api.core + +import java.time.Duration + +/** + * A delegating wrapper around a [Sleeper] that closes it once it's only phantom reachable. + * + * This class ensures the [Sleeper] is closed even if the user forgets to do it. + */ +internal class PhantomReachableSleeper(private val sleeper: Sleeper) : Sleeper { + + init { + closeWhenPhantomReachable(this, sleeper) + } + + override fun sleep(duration: Duration) = sleeper.sleep(duration) + + override suspend fun sleepAsync(duration: Duration) = sleeper.sleepAsync(duration) + + override fun close() = sleeper.close() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PrepareRequest.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PrepareRequest.kt new file mode 100644 index 00000000..0f94dc36 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/PrepareRequest.kt @@ -0,0 +1,16 @@ +package com.grid.api.core + +import com.grid.api.core.http.HttpRequest + +internal fun HttpRequest.prepare(clientOptions: ClientOptions, params: Params): HttpRequest = + toBuilder() + .putAllQueryParams(clientOptions.queryParams) + .replaceAllQueryParams(params._queryParams()) + .putAllHeaders(clientOptions.headers) + .replaceAllHeaders(params._headers()) + .build() + +internal suspend fun HttpRequest.prepareAsync(clientOptions: ClientOptions, params: Params) = + // This async version exists to make it easier to add async specific preparation logic in the + // future. + prepare(clientOptions, params) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Properties.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Properties.kt new file mode 100644 index 00000000..0c706861 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Properties.kt @@ -0,0 +1,42 @@ +@file:JvmName("Properties") + +package com.grid.api.core + +import com.grid.api.client.GridClient + +fun getOsArch(): String { + val osArch = System.getProperty("os.arch") + + return when (osArch) { + null -> "unknown" + "i386", + "x32", + "x86" -> "x32" + "amd64", + "x86_64" -> "x64" + "arm" -> "arm" + "aarch64" -> "arm64" + else -> "other:$osArch" + } +} + +fun getOsName(): String { + val osName = System.getProperty("os.name") + val vendorUrl = System.getProperty("java.vendor.url") + + return when { + osName == null -> "Unknown" + osName.startsWith("Linux") && vendorUrl == "http://www.android.com/" -> "Android" + osName.startsWith("Linux") -> "Linux" + osName.startsWith("Mac OS") -> "MacOS" + osName.startsWith("Windows") -> "Windows" + else -> "Other:$osName" + } +} + +fun getOsVersion(): String = System.getProperty("os.version", "unknown") + +fun getPackageVersion(): String = + GridClient::class.java.`package`.implementationVersion ?: "unknown" + +fun getJavaVersion(): String = System.getProperty("java.version", "unknown") diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/RequestOptions.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/RequestOptions.kt new file mode 100644 index 00000000..8a2bab42 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/RequestOptions.kt @@ -0,0 +1,45 @@ +package com.grid.api.core + +import java.time.Duration + +class RequestOptions private constructor(val responseValidation: Boolean?, val timeout: Timeout?) { + + companion object { + + private val NONE = builder().build() + + fun none() = NONE + + internal fun from(clientOptions: ClientOptions): RequestOptions = + builder() + .responseValidation(clientOptions.responseValidation) + .timeout(clientOptions.timeout) + .build() + + fun builder() = Builder() + } + + fun applyDefaults(options: RequestOptions): RequestOptions = + RequestOptions( + responseValidation = responseValidation ?: options.responseValidation, + timeout = + if (options.timeout != null && timeout != null) timeout.assign(options.timeout) + else timeout ?: options.timeout, + ) + + class Builder internal constructor() { + + private var responseValidation: Boolean? = null + private var timeout: Timeout? = null + + fun responseValidation(responseValidation: Boolean) = apply { + this.responseValidation = responseValidation + } + + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + fun build(): RequestOptions = RequestOptions(responseValidation, timeout) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Sleeper.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Sleeper.kt new file mode 100644 index 00000000..9da04bab --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Sleeper.kt @@ -0,0 +1,17 @@ +package com.grid.api.core + +import java.time.Duration + +/** + * An interface for delaying execution for a specified amount of time. + * + * Useful for testing and cleaning up resources. + */ +interface Sleeper : AutoCloseable { + + /** Synchronously pauses execution for the given [duration]. */ + fun sleep(duration: Duration) + + /** Asynchronously pauses execution for the given [duration]. */ + suspend fun sleepAsync(duration: Duration) +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Timeout.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Timeout.kt new file mode 100644 index 00000000..d69f8643 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Timeout.kt @@ -0,0 +1,155 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +import java.time.Duration +import java.util.Objects + +/** A class containing timeouts for various processing phases of a request. */ +class Timeout +private constructor( + private val connect: Duration?, + private val read: Duration?, + private val write: Duration?, + private val request: Duration?, +) { + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(): Duration = connect ?: Duration.ofMinutes(1) + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(): Duration = read ?: request() + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(): Duration = write ?: request() + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as well + * as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun request(): Duration = request ?: Duration.ofMinutes(1) + + fun toBuilder() = Builder().from(this) + + companion object { + + fun default() = builder().build() + + /** Returns a mutable builder for constructing an instance of [Timeout]. */ + fun builder() = Builder() + } + + /** A builder for [Timeout]. */ + class Builder internal constructor() { + + private var connect: Duration? = null + private var read: Duration? = null + private var write: Duration? = null + private var request: Duration? = null + + internal fun from(timeout: Timeout) = apply { + connect = timeout.connect + read = timeout.read + write = timeout.write + request = timeout.request + } + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(connect: Duration?) = apply { this.connect = connect } + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(read: Duration?) = apply { this.read = read } + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(write: Duration?) = apply { this.write = write } + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as + * well as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun request(request: Duration?) = apply { this.request = request } + + /** + * Returns an immutable instance of [Timeout]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Timeout = Timeout(connect, read, write, request) + } + + internal fun assign(target: Timeout): Timeout = + target + .toBuilder() + .apply { + connect?.let(this::connect) + read?.let(this::read) + write?.let(this::write) + request?.let(this::request) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Timeout && + connect == other.connect && + read == other.read && + write == other.write && + request == other.request + } + + override fun hashCode(): Int = Objects.hash(connect, read, write, request) + + override fun toString() = + "Timeout{connect=$connect, read=$read, write=$write, request=$request}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Utils.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Utils.kt new file mode 100644 index 00000000..6ee56118 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Utils.kt @@ -0,0 +1,102 @@ +@file:JvmName("Utils") + +package com.grid.api.core + +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.SortedMap +import java.util.concurrent.locks.Lock + +internal fun T?.getOrThrow(name: String): T = + this ?: throw GridInvalidDataException("`${name}` is not present") + +internal fun List.toImmutable(): List = + if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList()) + +internal fun Map.toImmutable(): Map = + if (isEmpty()) immutableEmptyMap() else Collections.unmodifiableMap(toMap()) + +internal fun immutableEmptyMap(): Map = Collections.emptyMap() + +internal fun , V> SortedMap.toImmutable(): SortedMap = + if (isEmpty()) Collections.emptySortedMap() + else Collections.unmodifiableSortedMap(toSortedMap(comparator())) + +/** + * Returns all elements that yield the largest value for the given function, or an empty list if + * there are zero elements. + * + * This is similar to [Sequence.maxByOrNull] except it returns _all_ elements that yield the largest + * value; not just the first one. + */ +internal fun > Sequence.allMaxBy(selector: (T) -> R): List { + var maxValue: R? = null + val maxElements = mutableListOf() + + val iterator = iterator() + while (iterator.hasNext()) { + val element = iterator.next() + val value = selector(element) + if (maxValue == null || value > maxValue) { + maxValue = value + maxElements.clear() + maxElements.add(element) + } else if (value == maxValue) { + maxElements.add(element) + } + } + + return maxElements +} + +/** + * Returns whether [this] is equal to [other]. + * + * This differs from [Object.equals] because it also deeply equates arrays based on their contents, + * even when there are arrays directly nested within other arrays. + */ +internal infix fun Any?.contentEquals(other: Any?): Boolean = + arrayOf(this).contentDeepEquals(arrayOf(other)) + +/** + * Returns a hash of the given sequence of [values]. + * + * This differs from [java.util.Objects.hash] because it also deeply hashes arrays based on their + * contents, even when there are arrays directly nested within other arrays. + */ +internal fun contentHash(vararg values: Any?): Int = values.contentDeepHashCode() + +/** + * Returns a [String] representation of [this]. + * + * This differs from [Object.toString] because it also deeply stringifies arrays based on their + * contents, even when there are arrays directly nested within other arrays. + */ +internal fun Any?.contentToString(): String { + var string = arrayOf(this).contentDeepToString() + if (string.startsWith('[')) { + string = string.substring(1) + } + if (string.endsWith(']')) { + string = string.substring(0, string.length - 1) + } + return string +} + +internal interface Enum + +/** + * Executes a suspending block of code while holding this lock. + * + * @param T the return type of the action + * @param action the suspending function to execute while holding the lock + * @return the result of executing the action + */ +internal suspend fun Lock.withLockAsync(action: suspend () -> T): T { + lock() + return try { + action() + } finally { + unlock() + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt new file mode 100644 index 00000000..a4aa3fd4 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Values.kt @@ -0,0 +1,694 @@ +package com.grid.api.core + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.BeanProperty +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.node.JsonNodeType.ARRAY +import com.fasterxml.jackson.databind.node.JsonNodeType.BINARY +import com.fasterxml.jackson.databind.node.JsonNodeType.BOOLEAN +import com.fasterxml.jackson.databind.node.JsonNodeType.MISSING +import com.fasterxml.jackson.databind.node.JsonNodeType.NULL +import com.fasterxml.jackson.databind.node.JsonNodeType.NUMBER +import com.fasterxml.jackson.databind.node.JsonNodeType.OBJECT +import com.fasterxml.jackson.databind.node.JsonNodeType.POJO +import com.fasterxml.jackson.databind.node.JsonNodeType.STRING +import com.fasterxml.jackson.databind.ser.std.NullSerializer +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.errors.GridInvalidDataException +import java.io.InputStream +import java.util.* +import kotlin.reflect.KClass + +/** + * A class representing a serializable JSON field. + * + * It can either be a [KnownValue] value of type [T], matching the type the SDK expects, or an + * arbitrary JSON value that bypasses the type system (via [JsonValue]). + */ +@JsonDeserialize(using = JsonField.Deserializer::class) +sealed class JsonField { + + /** + * Returns whether this field is missing, which means it will be omitted from the serialized + * JSON entirely. + */ + fun isMissing(): Boolean = this is JsonMissing + + /** Whether this field is explicitly set to `null`. */ + fun isNull(): Boolean = this is JsonNull + + /** + * Returns this field's "known" value, meaning it matches the type the SDK expects, or null if + * this field contains an arbitrary [JsonValue]. + * + * This is the opposite of [asUnknown]. + */ + fun asKnown(): T? = (this as? KnownValue)?.value + + /** + * Returns this field's arbitrary [JsonValue], meaning it mismatches the type the SDK expects, + * or null if this field contains a "known" value. + * + * This is the opposite of [asKnown]. + */ + fun asUnknown(): JsonValue? = this as? JsonValue + + /** + * Returns this field's boolean value, or null if it doesn't contain a boolean. + * + * This method checks for both a [KnownValue] containing a boolean and for [JsonBoolean]. + */ + fun asBoolean(): Boolean? = + when (this) { + is JsonBoolean -> value + is KnownValue -> value as? Boolean + else -> null + } + + /** + * Returns this field's numerical value, or null if it doesn't contain a number. + * + * This method checks for both a [KnownValue] containing a number and for [JsonNumber]. + */ + fun asNumber(): Number? = + when (this) { + is JsonNumber -> value + is KnownValue -> value as? Number + else -> null + } + + /** + * Returns this field's string value, or null if it doesn't contain a string. + * + * This method checks for both a [KnownValue] containing a string and for [JsonString]. + */ + fun asString(): String? = + when (this) { + is JsonString -> value + is KnownValue -> value as? String + else -> null + } + + fun asStringOrThrow(): String = + asString() ?: throw GridInvalidDataException("Value is not a string") + + /** + * Returns this field's list value, or null if it doesn't contain a list. + * + * This method checks for both a [KnownValue] containing a list and for [JsonArray]. + */ + fun asArray(): List? = + when (this) { + is JsonArray -> values + is KnownValue -> + (value as? List<*>)?.map { + try { + JsonValue.from(it) + } catch (e: IllegalArgumentException) { + // The known value is a list, but not all items are convertible to + // `JsonValue`. + return null + } + } + else -> null + } + + /** + * Returns this field's map value, or null if it doesn't contain a map. + * + * This method checks for both a [KnownValue] containing a map and for [JsonObject]. + */ + fun asObject(): Map? = + when (this) { + is JsonObject -> values + is KnownValue -> + (value as? Map<*, *>) + ?.map { (key, value) -> + if (key !is String) { + return null + } + + val jsonValue = + try { + JsonValue.from(value) + } catch (e: IllegalArgumentException) { + // The known value is a map, but not all values are convertible to + // `JsonValue`. + return null + } + + key to jsonValue + } + ?.toMap() + else -> null + } + + internal fun getRequired(name: String): T = + when (this) { + is KnownValue -> value + is JsonMissing -> throw GridInvalidDataException("`$name` is not set") + is JsonNull -> throw GridInvalidDataException("`$name` is null") + else -> throw GridInvalidDataException("`$name` is invalid, received $this") + } + + internal fun getNullable(name: String): T? = + when (this) { + is KnownValue -> value + is JsonMissing -> null + is JsonNull -> null + else -> throw GridInvalidDataException("`$name` is invalid, received $this") + } + + internal fun map(transform: (T) -> R): JsonField = + when (this) { + is KnownValue -> KnownValue.of(transform(value)) + is JsonValue -> this + } + + internal fun accept(consume: (T) -> Unit) { + asKnown()?.let(consume) + } + + /** Returns the result of calling the [visitor] method corresponding to this field's state. */ + fun accept(visitor: Visitor): R = + when (this) { + is KnownValue -> visitor.visitKnown(value) + is JsonValue -> accept(visitor as JsonValue.Visitor) + } + + /** + * An interface that defines how to map each possible state of a `JsonField` to a value of + * type [R]. + */ + interface Visitor : JsonValue.Visitor { + + fun visitKnown(value: T): R = visitDefault() + } + + companion object { + + /** Returns a [JsonField] containing the given "known" [value]. */ + fun of(value: T): JsonField = KnownValue.of(value) + + /** + * Returns a [JsonField] containing the given "known" [value], or [JsonNull] if [value] is + * null. + */ + fun ofNullable(value: T?): JsonField = + when (value) { + null -> JsonNull.of() + else -> KnownValue.of(value) + } + } + + /** + * This class is a Jackson filter that can be used to exclude missing properties from objects. + * This filter should not be used directly and should instead use the @ExcludeMissing + * annotation. + */ + class IsMissing { + + override fun equals(other: Any?): Boolean = other is JsonMissing + + override fun hashCode(): Int = Objects.hash() + } + + class Deserializer(private val type: JavaType? = null) : + BaseDeserializer>(JsonField::class) { + + override fun createContextual( + context: DeserializationContext, + property: BeanProperty?, + ): JsonDeserializer> = Deserializer(context.contextualType?.containedType(0)) + + override fun ObjectCodec.deserialize(node: JsonNode): JsonField<*> = + type?.let { tryDeserialize(node, type) }?.let { of(it) } + ?: JsonValue.fromJsonNode(node) + + override fun getNullValue(context: DeserializationContext): JsonField<*> = JsonNull.of() + } +} + +/** + * A class representing an arbitrary JSON value. + * + * It is immutable and assignable to any [JsonField], regardless of its expected type (i.e. its + * generic type argument). + */ +@JsonDeserialize(using = JsonValue.Deserializer::class) +sealed class JsonValue : JsonField() { + + inline fun convert(): R? = convert(jacksonTypeRef()) + + fun convert(type: TypeReference): R? = JSON_MAPPER.convertValue(this, type) + + fun convert(type: KClass): R? = JSON_MAPPER.convertValue(this, type.java) + + /** Returns the result of calling the [visitor] method corresponding to this value's variant. */ + fun accept(visitor: Visitor): R = + when (this) { + is JsonMissing -> visitor.visitMissing() + is JsonNull -> visitor.visitNull() + is JsonBoolean -> visitor.visitBoolean(value) + is JsonNumber -> visitor.visitNumber(value) + is JsonString -> visitor.visitString(value) + is JsonArray -> visitor.visitArray(values) + is JsonObject -> visitor.visitObject(values) + } + + /** + * An interface that defines how to map each variant state of a [JsonValue] to a value of type + * [R]. + */ + interface Visitor { + + fun visitNull(): R = visitDefault() + + fun visitMissing(): R = visitDefault() + + fun visitBoolean(value: Boolean): R = visitDefault() + + fun visitNumber(value: Number): R = visitDefault() + + fun visitString(value: String): R = visitDefault() + + fun visitArray(values: List): R = visitDefault() + + fun visitObject(values: Map): R = visitDefault() + + /** + * The default implementation for unimplemented visitor methods. + * + * @throws IllegalArgumentException in the default implementation. + */ + fun visitDefault(): R = throw IllegalArgumentException("Unexpected value") + } + + companion object { + + private val JSON_MAPPER = jsonMapper() + + /** + * Converts the given [value] to a [JsonValue]. + * + * This method works best on primitive types, [List] values, [Map] values, and nested + * combinations of these. For example: + * ```kotlin + * // Create primitive JSON values + * val nullValue: JsonValue = JsonValue.from(null) + * val booleanValue: JsonValue = JsonValue.from(true) + * val numberValue: JsonValue = JsonValue.from(42) + * val stringValue: JsonValue = JsonValue.from("Hello World!") + * + * // Create a JSON array value equivalent to `["Hello", "World"]` + * val arrayValue: JsonValue = JsonValue.from(listOf("Hello", "World")) + * + * // Create a JSON object value equivalent to `{ "a": 1, "b": 2 }` + * val objectValue: JsonValue = JsonValue.from(mapOf( + * "a" to 1, + * "b" to 2, + * )) + * + * // Create an arbitrarily nested JSON equivalent to: + * // { + * // "a": [1, 2], + * // "b": [3, 4] + * // } + * val complexValue: JsonValue = JsonValue.from(mapOf( + * "a" to listOf(1, 2), + * "b" to listOf(3, 4), + * )) + * ``` + * + * @throws IllegalArgumentException if [value] is not JSON serializable. + */ + fun from(value: Any?): JsonValue = + when (value) { + null -> JsonNull.of() + is JsonValue -> value + else -> JSON_MAPPER.convertValue(value, JsonValue::class.java) + } + + /** + * Returns a [JsonValue] converted from the given Jackson [JsonNode]. + * + * @throws IllegalStateException for unsupported node types. + */ + fun fromJsonNode(node: JsonNode): JsonValue = + when (node.nodeType) { + MISSING -> JsonMissing.of() + NULL -> JsonNull.of() + BOOLEAN -> JsonBoolean.of(node.booleanValue()) + NUMBER -> JsonNumber.of(node.numberValue()) + STRING -> JsonString.of(node.textValue()) + ARRAY -> + JsonArray.of(node.elements().asSequence().map { fromJsonNode(it) }.toList()) + OBJECT -> + JsonObject.of( + node.fields().asSequence().map { it.key to fromJsonNode(it.value) }.toMap() + ) + BINARY, + POJO, + null -> throw IllegalStateException("Unexpected JsonNode type: ${node.nodeType}") + } + } + + class Deserializer : BaseDeserializer(JsonValue::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): JsonValue = fromJsonNode(node) + + override fun getNullValue(context: DeserializationContext?): JsonValue = JsonNull.of() + } +} + +/** + * A class representing a "known" JSON serializable value of type [T], matching the type the SDK + * expects. + * + * It is assignable to `JsonField`. + */ +class KnownValue +private constructor( + @com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: T +) : JsonField() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is KnownValue<*> && value contentEquals other.value + } + + override fun hashCode() = contentHash(value) + + override fun toString() = value.contentToString() + + companion object { + + /** Returns a [KnownValue] containing the given [value]. */ + @JsonCreator fun of(value: T) = KnownValue(value) + } +} + +/** + * A [JsonValue] representing an omitted JSON field. + * + * An instance of this class will cause a JSON field to be omitted from the serialized JSON + * entirely. + */ +@JsonSerialize(using = JsonMissing.Serializer::class) +class JsonMissing : JsonValue() { + + override fun toString() = "" + + companion object { + + private val INSTANCE: JsonMissing = JsonMissing() + + /** Returns the singleton instance of [JsonMissing]. */ + fun of() = INSTANCE + } + + class Serializer : BaseSerializer(JsonMissing::class) { + + override fun serialize( + value: JsonMissing, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + throw IllegalStateException("JsonMissing cannot be serialized") + } + } +} + +/** A [JsonValue] representing a JSON `null` value. */ +@JsonSerialize(using = NullSerializer::class) +class JsonNull : JsonValue() { + + override fun toString() = "null" + + companion object { + + private val INSTANCE: JsonNull = JsonNull() + + /** Returns the singleton instance of [JsonMissing]. */ + @JsonCreator fun of() = INSTANCE + } +} + +/** A [JsonValue] representing a JSON boolean value. */ +class JsonBoolean +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: Boolean +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonBoolean && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + companion object { + + /** Returns a [JsonBoolean] containing the given [value]. */ + @JsonCreator fun of(value: Boolean) = JsonBoolean(value) + } +} + +/** A [JsonValue] representing a JSON number value. */ +class JsonNumber +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: Number +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonNumber && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + companion object { + + /** Returns a [JsonNumber] containing the given [value]. */ + @JsonCreator fun of(value: Number) = JsonNumber(value) + } +} + +/** A [JsonValue] representing a JSON string value. */ +class JsonString +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: String +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonString && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value + + companion object { + + /** Returns a [JsonString] containing the given [value]. */ + @JsonCreator fun of(value: String) = JsonString(value) + } +} + +/** A [JsonValue] representing a JSON array value. */ +class JsonArray +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue + @get:JvmName("values") + val values: List +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonArray && values == other.values + } + + override fun hashCode() = values.hashCode() + + override fun toString() = values.toString() + + companion object { + + /** Returns a [JsonArray] containing the given [values]. */ + @JsonCreator fun of(values: List) = JsonArray(values.toImmutable()) + } +} + +/** A [JsonValue] representing a JSON object value. */ +class JsonObject +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue + @get:JvmName("values") + val values: Map +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonObject && values == other.values + } + + override fun hashCode() = values.hashCode() + + override fun toString() = values.toString() + + companion object { + + /** Returns a [JsonObject] containing the given [values]. */ + @JsonCreator fun of(values: Map) = JsonObject(values.toImmutable()) + } +} + +/** A Jackson annotation for excluding fields set to [JsonMissing] from the serialized JSON. */ +@JacksonAnnotationsInside +@JsonInclude(JsonInclude.Include.CUSTOM, valueFilter = JsonField.IsMissing::class) +annotation class ExcludeMissing + +/** A class representing a field in a `multipart/form-data` request. */ +class MultipartField +private constructor( + /** A [JsonField] value, which will be serialized to zero or more parts. */ + @get:com.fasterxml.jackson.annotation.JsonValue val value: JsonField, + /** A content type for the serialized parts. */ + val contentType: String, + /** Returns the filename directive that will be included in the serialized field. */ + val filename: String?, +) { + + companion object { + + /** + * Returns a [MultipartField] containing the given [value] as a [KnownValue]. + * + * [contentType] will be set to `application/octet-stream` if [value] is binary data, or + * `text/plain; charset=utf-8` otherwise. + */ + fun of(value: T?) = builder().value(value).build() + + /** + * Returns a [MultipartField] containing the given [value]. + * + * [contentType] will be set to `application/octet-stream` if [value] is binary data, or + * `text/plain; charset=utf-8` otherwise. + */ + fun of(value: JsonField) = builder().value(value).build() + + /** + * Returns a mutable builder for constructing an instance of [MultipartField]. + * + * The following fields are required: + * ```kotlin + * .value() + * ``` + * + * If [contentType] is unset, then it will be set to `application/octet-stream` if [value] + * is binary data, or `text/plain; charset=utf-8` otherwise. + */ + fun builder() = Builder() + } + + internal fun map(transform: (T) -> R): MultipartField = + builder().value(value.map(transform)).contentType(contentType).filename(filename).build() + + /** A builder for [MultipartField]. */ + class Builder internal constructor() { + + private var value: JsonField? = null + private var contentType: String? = null + private var filename: String? = null + + fun value(value: JsonField) = apply { this.value = value } + + fun value(value: T?) = value(JsonField.ofNullable(value)) + + fun contentType(contentType: String) = apply { this.contentType = contentType } + + fun filename(filename: String?) = apply { this.filename = filename } + + /** + * Returns an immutable instance of [MultipartField]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .value() + * ``` + * + * If [contentType] is unset, then it will be set to `application/octet-stream` if [value] + * is binary data, or `text/plain; charset=utf-8` otherwise. + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MultipartField { + val value = checkRequired("value", value) + return MultipartField( + value, + contentType + ?: if ( + value is KnownValue && + (value.value is InputStream || value.value is ByteArray) + ) + "application/octet-stream" + else "text/plain; charset=utf-8", + filename, + ) + } + } + + private val hashCode: Int by lazy { contentHash(value, contentType, filename) } + + override fun hashCode(): Int = hashCode + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is MultipartField<*> && + value == other.value && + contentType == other.contentType && + filename == other.filename + } + + override fun toString(): String = + "MultipartField{value=$value, contentType=$contentType, filename=$filename}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/EmptyHandler.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/EmptyHandler.kt new file mode 100644 index 00000000..4f783ace --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/EmptyHandler.kt @@ -0,0 +1,12 @@ +@file:JvmName("EmptyHandler") + +package com.grid.api.core.handlers + +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler + +internal fun emptyHandler(): Handler = EmptyHandlerInternal + +private object EmptyHandlerInternal : Handler { + override fun handle(response: HttpResponse): Void? = null +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/ErrorHandler.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/ErrorHandler.kt new file mode 100644 index 00000000..a45f8943 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/ErrorHandler.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core.handlers + +import com.fasterxml.jackson.databind.json.JsonMapper +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.errors.BadRequestException +import com.grid.api.errors.InternalServerException +import com.grid.api.errors.NotFoundException +import com.grid.api.errors.PermissionDeniedException +import com.grid.api.errors.RateLimitException +import com.grid.api.errors.UnauthorizedException +import com.grid.api.errors.UnexpectedStatusCodeException +import com.grid.api.errors.UnprocessableEntityException + +internal fun errorBodyHandler(jsonMapper: JsonMapper): Handler { + val handler = jsonHandler(jsonMapper) + + return object : Handler { + override fun handle(response: HttpResponse): JsonValue = + try { + handler.handle(response) + } catch (e: Exception) { + JsonMissing.of() + } + } +} + +internal fun errorHandler(errorBodyHandler: Handler): Handler = + object : Handler { + override fun handle(response: HttpResponse): HttpResponse = + when (val statusCode = response.statusCode()) { + in 200..299 -> response + 400 -> + throw BadRequestException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 401 -> + throw UnauthorizedException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 403 -> + throw PermissionDeniedException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 404 -> + throw NotFoundException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 422 -> + throw UnprocessableEntityException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 429 -> + throw RateLimitException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + in 500..599 -> + throw InternalServerException.builder() + .statusCode(statusCode) + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + else -> + throw UnexpectedStatusCodeException.builder() + .statusCode(statusCode) + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + } + } diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/JsonHandler.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/JsonHandler.kt new file mode 100644 index 00000000..d6a13d3f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/JsonHandler.kt @@ -0,0 +1,20 @@ +@file:JvmName("JsonHandler") + +package com.grid.api.core.handlers + +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.errors.GridInvalidDataException + +internal inline fun jsonHandler(jsonMapper: JsonMapper): Handler = + object : Handler { + override fun handle(response: HttpResponse): T { + try { + return jsonMapper.readValue(response.body(), jacksonTypeRef()) + } catch (e: Exception) { + throw GridInvalidDataException("Error reading response", e) + } + } + } diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/StringHandler.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/StringHandler.kt new file mode 100644 index 00000000..36c556ed --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/handlers/StringHandler.kt @@ -0,0 +1,13 @@ +@file:JvmName("StringHandler") + +package com.grid.api.core.handlers + +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler + +internal fun stringHandler(): Handler = StringHandlerInternal + +private object StringHandlerInternal : Handler { + override fun handle(response: HttpResponse): String = + response.body().readBytes().toString(Charsets.UTF_8) +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/Headers.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/Headers.kt new file mode 100644 index 00000000..315e8749 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/Headers.kt @@ -0,0 +1,111 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core.http + +import com.grid.api.core.JsonArray +import com.grid.api.core.JsonBoolean +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonNull +import com.grid.api.core.JsonNumber +import com.grid.api.core.JsonObject +import com.grid.api.core.JsonString +import com.grid.api.core.JsonValue +import com.grid.api.core.toImmutable +import java.util.TreeMap + +class Headers private constructor(private val map: Map>, val size: Int) { + + fun isEmpty(): Boolean = map.isEmpty() + + fun names(): Set = map.keys + + fun values(name: String): List = map[name].orEmpty() + + fun toBuilder(): Builder = Builder().putAll(map) + + companion object { + + fun builder() = Builder() + } + + class Builder internal constructor() { + + private val map: MutableMap> = + TreeMap(String.CASE_INSENSITIVE_ORDER) + private var size: Int = 0 + + fun put(name: String, value: JsonValue): Builder = apply { + when (value) { + is JsonMissing, + is JsonNull -> {} + is JsonBoolean -> put(name, value.value.toString()) + is JsonNumber -> put(name, value.value.toString()) + is JsonString -> put(name, value.value) + is JsonArray -> value.values.forEach { put(name, it) } + is JsonObject -> + value.values.forEach { (nestedName, value) -> put("$name.$nestedName", value) } + } + } + + fun put(name: String, value: String) = apply { + map.getOrPut(name) { mutableListOf() }.add(value) + size++ + } + + fun put(name: String, values: Iterable) = apply { values.forEach { put(name, it) } } + + fun putAll(headers: Map>) = apply { headers.forEach(::put) } + + fun putAll(headers: Headers) = apply { + headers.names().forEach { put(it, headers.values(it)) } + } + + fun replace(name: String, value: String) = apply { + remove(name) + put(name, value) + } + + fun replace(name: String, values: Iterable) = apply { + remove(name) + put(name, values) + } + + fun replaceAll(headers: Map>) = apply { + headers.forEach(::replace) + } + + fun replaceAll(headers: Headers) = apply { + headers.names().forEach { replace(it, headers.values(it)) } + } + + fun remove(name: String) = apply { size -= map.remove(name).orEmpty().size } + + fun removeAll(names: Set) = apply { names.forEach(::remove) } + + fun clear() = apply { + map.clear() + size = 0 + } + + fun build() = + Headers( + map.mapValuesTo(TreeMap(String.CASE_INSENSITIVE_ORDER)) { (_, values) -> + values.toImmutable() + } + .toImmutable(), + size, + ) + } + + override fun hashCode(): Int = map.hashCode() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Headers && map == other.map + } + + override fun toString(): String = "Headers{map=$map}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpClient.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpClient.kt new file mode 100644 index 00000000..a1f66735 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpClient.kt @@ -0,0 +1,17 @@ +package com.grid.api.core.http + +import com.grid.api.core.RequestOptions +import java.lang.AutoCloseable + +interface HttpClient : AutoCloseable { + + fun execute( + request: HttpRequest, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + suspend fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpMethod.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpMethod.kt new file mode 100644 index 00000000..0f81048c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpMethod.kt @@ -0,0 +1,13 @@ +package com.grid.api.core.http + +enum class HttpMethod { + GET, + HEAD, + POST, + PUT, + DELETE, + CONNECT, + OPTIONS, + TRACE, + PATCH, +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequest.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequest.kt new file mode 100644 index 00000000..33a181d6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequest.kt @@ -0,0 +1,175 @@ +package com.grid.api.core.http + +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import java.net.URLEncoder + +class HttpRequest +private constructor( + val method: HttpMethod, + val baseUrl: String, + val pathSegments: List, + val headers: Headers, + val queryParams: QueryParams, + val body: HttpRequestBody?, +) { + + fun url(): String = buildString { + append(baseUrl) + + pathSegments.forEach { segment -> + if (!endsWith("/")) { + append("/") + } + append(URLEncoder.encode(segment, "UTF-8")) + } + + if (queryParams.isEmpty()) { + return@buildString + } + + append("?") + var isFirst = true + queryParams.keys().forEach { key -> + queryParams.values(key).forEach { value -> + if (!isFirst) { + append("&") + } + append(URLEncoder.encode(key, "UTF-8")) + append("=") + append(URLEncoder.encode(value, "UTF-8")) + isFirst = false + } + } + } + + fun toBuilder(): Builder = Builder().from(this) + + override fun toString(): String = + "HttpRequest{method=$method, baseUrl=$baseUrl, pathSegments=$pathSegments, headers=$headers, queryParams=$queryParams, body=$body}" + + companion object { + fun builder() = Builder() + } + + class Builder internal constructor() { + + private var method: HttpMethod? = null + private var baseUrl: String? = null + private var pathSegments: MutableList = mutableListOf() + private var headers: Headers.Builder = Headers.builder() + private var queryParams: QueryParams.Builder = QueryParams.builder() + private var body: HttpRequestBody? = null + + internal fun from(request: HttpRequest) = apply { + method = request.method + baseUrl = request.baseUrl + pathSegments = request.pathSegments.toMutableList() + headers = request.headers.toBuilder() + queryParams = request.queryParams.toBuilder() + body = request.body + } + + fun method(method: HttpMethod) = apply { this.method = method } + + fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl } + + fun addPathSegment(pathSegment: String) = apply { pathSegments.add(pathSegment) } + + fun addPathSegments(vararg pathSegments: String) = apply { + this.pathSegments.addAll(pathSegments) + } + + fun headers(headers: Headers) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun headers(headers: Map>) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun putHeader(name: String, value: String) = apply { headers.put(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { headers.put(name, values) } + + fun putAllHeaders(headers: Headers) = apply { this.headers.putAll(headers) } + + fun putAllHeaders(headers: Map>) = apply { + this.headers.putAll(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { headers.replace(name, value) } + + fun replaceHeaders(name: String, values: Iterable) = apply { + headers.replace(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { this.headers.replaceAll(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + this.headers.replaceAll(headers) + } + + fun removeHeaders(name: String) = apply { headers.remove(name) } + + fun removeAllHeaders(names: Set) = apply { headers.removeAll(names) } + + fun queryParams(queryParams: QueryParams) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun queryParams(queryParams: Map>) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { queryParams.put(key, value) } + + fun putQueryParams(key: String, values: Iterable) = apply { + queryParams.put(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.putAll(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + this.queryParams.putAll(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + queryParams.replace(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + queryParams.replace(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun removeQueryParams(key: String) = apply { queryParams.remove(key) } + + fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) } + + fun body(body: HttpRequestBody) = apply { this.body = body } + + fun build(): HttpRequest = + HttpRequest( + checkRequired("method", method), + checkRequired("baseUrl", baseUrl), + pathSegments.toImmutable(), + headers.build(), + queryParams.build(), + body, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBodies.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBodies.kt new file mode 100644 index 00000000..17d0c32e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBodies.kt @@ -0,0 +1,122 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core.http + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.node.JsonNodeType +import com.grid.api.core.MultipartField +import com.grid.api.errors.GridInvalidDataException +import java.io.InputStream +import java.io.OutputStream +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder +import org.apache.hc.core5.http.ContentType +import org.apache.hc.core5.http.HttpEntity + +internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody = + object : HttpRequestBody { + private val bytes: ByteArray by lazy { jsonMapper.writeValueAsBytes(value) } + + override fun writeTo(outputStream: OutputStream) = outputStream.write(bytes) + + override fun contentType(): String = "application/json" + + override fun contentLength(): Long = bytes.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} + } + +internal fun multipartFormData( + jsonMapper: JsonMapper, + fields: Map>, +): HttpRequestBody = + object : HttpRequestBody { + private val entity: HttpEntity by lazy { + MultipartEntityBuilder.create() + .apply { + fields.forEach { (name, field) -> + val knownValue = field.value.asKnown() + val parts = + if (knownValue is InputStream) { + // Read directly from the `InputStream` instead of reading it all + // into memory due to the `jsonMapper` serialization below. + sequenceOf(name to knownValue) + } else { + val node = jsonMapper.valueToTree(field.value) + serializePart(name, node) + } + + parts.forEach { (name, bytes) -> + addBinaryBody( + name, + bytes, + ContentType.parseLenient(field.contentType), + field.filename, + ) + } + } + } + .build() + } + + private fun serializePart( + name: String, + node: JsonNode, + ): Sequence> = + when (node.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> emptySequence() + JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream()) + JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream()) + JsonNodeType.BOOLEAN -> + sequenceOf(name to node.booleanValue().toString().inputStream()) + JsonNodeType.NUMBER -> + sequenceOf(name to node.numberValue().toString().inputStream()) + JsonNodeType.ARRAY -> + sequenceOf( + name to + node + .elements() + .asSequence() + .mapNotNull { element -> + when (element.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> null + JsonNodeType.STRING -> node.textValue() + JsonNodeType.BOOLEAN -> node.booleanValue().toString() + JsonNodeType.NUMBER -> node.numberValue().toString() + null, + JsonNodeType.BINARY, + JsonNodeType.ARRAY, + JsonNodeType.OBJECT, + JsonNodeType.POJO -> + throw GridInvalidDataException( + "Unexpected JsonNode type in array: ${node.nodeType}" + ) + } + } + .joinToString(",") + .inputStream() + ) + JsonNodeType.OBJECT -> + node.fields().asSequence().flatMap { (key, value) -> + serializePart("$name[$key]", value) + } + JsonNodeType.POJO, + null -> throw GridInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") + } + + private fun String.inputStream(): InputStream = toByteArray().inputStream() + + override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream) + + override fun contentType(): String = entity.contentType + + override fun contentLength(): Long = entity.contentLength + + override fun repeatable(): Boolean = entity.isRepeatable + + override fun close() = entity.close() + } diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBody.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBody.kt new file mode 100644 index 00000000..598f4bb7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpRequestBody.kt @@ -0,0 +1,22 @@ +package com.grid.api.core.http + +import java.io.OutputStream +import java.lang.AutoCloseable + +interface HttpRequestBody : AutoCloseable { + + fun writeTo(outputStream: OutputStream) + + fun contentType(): String? + + fun contentLength(): Long + + /** + * Determines if a request can be repeated in a meaningful way, for example before doing a + * retry. + * + * The most typical case when a request can't be retried is if the request body is being + * streamed. In this case the body data isn't available on subsequent attempts. + */ + fun repeatable(): Boolean +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponse.kt new file mode 100644 index 00000000..adcba8b2 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponse.kt @@ -0,0 +1,19 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core.http + +import java.io.InputStream + +interface HttpResponse : AutoCloseable { + + fun statusCode(): Int + + fun headers(): Headers + + fun body(): InputStream + + interface Handler { + + fun handle(response: HttpResponse): T + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponseFor.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponseFor.kt new file mode 100644 index 00000000..fe4482c6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/HttpResponseFor.kt @@ -0,0 +1,24 @@ +package com.grid.api.core.http + +import java.io.InputStream + +interface HttpResponseFor : HttpResponse { + + fun parse(): T +} + +internal fun HttpResponse.parseable(parse: () -> T): HttpResponseFor = + object : HttpResponseFor { + + private val parsed: T by lazy { parse() } + + override fun parse(): T = parsed + + override fun statusCode(): Int = this@parseable.statusCode() + + override fun headers(): Headers = this@parseable.headers() + + override fun body(): InputStream = this@parseable.body() + + override fun close() = this@parseable.close() + } diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingHttpClient.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingHttpClient.kt new file mode 100644 index 00000000..944c7d47 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingHttpClient.kt @@ -0,0 +1,25 @@ +package com.grid.api.core.http + +import com.grid.api.core.RequestOptions +import com.grid.api.core.closeWhenPhantomReachable + +/** + * A delegating wrapper around an `HttpClient` that closes it once it's only phantom reachable. + * + * This class ensures the `HttpClient` is closed even if the user forgets to close it. + */ +internal class PhantomReachableClosingHttpClient(private val httpClient: HttpClient) : HttpClient { + init { + closeWhenPhantomReachable(this, httpClient) + } + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse = + httpClient.execute(request, requestOptions) + + override suspend fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = httpClient.executeAsync(request, requestOptions) + + override fun close() = httpClient.close() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingStreamResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingStreamResponse.kt new file mode 100644 index 00000000..3d0d3898 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/PhantomReachableClosingStreamResponse.kt @@ -0,0 +1,20 @@ +package com.grid.api.core.http + +import com.grid.api.core.closeWhenPhantomReachable + +/** + * A delegating wrapper around a `StreamResponse` that closes it once it's only phantom reachable. + * + * This class ensures the `StreamResponse` is closed even if the user forgets to close it. + */ +internal class PhantomReachableClosingStreamResponse( + private val streamResponse: StreamResponse +) : StreamResponse { + init { + closeWhenPhantomReachable(this, streamResponse) + } + + override fun asSequence(): Sequence = streamResponse.asSequence() + + override fun close() = streamResponse.close() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/QueryParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/QueryParams.kt new file mode 100644 index 00000000..5d7ebfd0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/QueryParams.kt @@ -0,0 +1,125 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core.http + +import com.grid.api.core.JsonArray +import com.grid.api.core.JsonBoolean +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonNull +import com.grid.api.core.JsonNumber +import com.grid.api.core.JsonObject +import com.grid.api.core.JsonString +import com.grid.api.core.JsonValue +import com.grid.api.core.toImmutable + +class QueryParams private constructor(private val map: Map>, val size: Int) { + + fun isEmpty(): Boolean = map.isEmpty() + + fun keys(): Set = map.keys + + fun values(key: String): List = map[key].orEmpty() + + fun toBuilder(): Builder = Builder().putAll(map) + + companion object { + + fun builder() = Builder() + } + + class Builder internal constructor() { + + private val map: MutableMap> = mutableMapOf() + private var size: Int = 0 + + fun put(key: String, value: JsonValue): Builder = apply { + when (value) { + is JsonMissing, + is JsonNull -> {} + is JsonBoolean -> put(key, value.value.toString()) + is JsonNumber -> put(key, value.value.toString()) + is JsonString -> put(key, value.value) + is JsonArray -> + put( + key, + value.values + .asSequence() + .mapNotNull { + when (it) { + is JsonMissing, + is JsonNull -> null + is JsonBoolean -> it.value.toString() + is JsonNumber -> it.value.toString() + is JsonString -> it.value + is JsonArray, + is JsonObject -> + throw IllegalArgumentException( + "Cannot comma separate non-primitives in query params" + ) + } + } + .joinToString(","), + ) + is JsonObject -> + value.values.forEach { (nestedKey, value) -> put("$key[$nestedKey]", value) } + } + } + + fun put(key: String, value: String) = apply { + map.getOrPut(key) { mutableListOf() }.add(value) + size++ + } + + fun put(key: String, values: Iterable) = apply { values.forEach { put(key, it) } } + + fun putAll(queryParams: Map>) = apply { + queryParams.forEach(::put) + } + + fun putAll(queryParams: QueryParams) = apply { + queryParams.keys().forEach { put(it, queryParams.values(it)) } + } + + fun replace(key: String, value: String) = apply { + remove(key) + put(key, value) + } + + fun replace(key: String, values: Iterable) = apply { + remove(key) + put(key, values) + } + + fun replaceAll(queryParams: Map>) = apply { + queryParams.forEach(::replace) + } + + fun replaceAll(queryParams: QueryParams) = apply { + queryParams.keys().forEach { replace(it, queryParams.values(it)) } + } + + fun remove(key: String) = apply { size -= map.remove(key).orEmpty().size } + + fun removeAll(keys: Set) = apply { keys.forEach(::remove) } + + fun clear() = apply { + map.clear() + size = 0 + } + + fun build() = + QueryParams(map.mapValues { (_, values) -> values.toImmutable() }.toImmutable(), size) + } + + override fun hashCode(): Int = map.hashCode() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QueryParams && map == other.map + } + + override fun toString(): String = "QueryParams{map=$map}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/RetryingHttpClient.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/RetryingHttpClient.kt new file mode 100644 index 00000000..66b92b86 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/RetryingHttpClient.kt @@ -0,0 +1,248 @@ +package com.grid.api.core.http + +import com.grid.api.core.DefaultSleeper +import com.grid.api.core.RequestOptions +import com.grid.api.core.Sleeper +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridIoException +import com.grid.api.errors.GridRetryableException +import java.io.IOException +import java.time.Clock +import java.time.Duration +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeParseException +import java.time.temporal.ChronoUnit +import java.util.UUID +import java.util.concurrent.ThreadLocalRandom +import java.util.concurrent.TimeUnit +import kotlin.math.min +import kotlin.math.pow + +class RetryingHttpClient +private constructor( + private val httpClient: HttpClient, + private val sleeper: Sleeper, + private val clock: Clock, + private val maxRetries: Int, + private val idempotencyHeader: String?, +) : HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + var modifiedRequest = maybeAddIdempotencyHeader(request) + + // Don't send the current retry count in the headers if the caller set their own value. + val shouldSendRetryCount = + !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count") + + var retries = 0 + + while (true) { + if (shouldSendRetryCount) { + modifiedRequest = setRetryCountHeader(modifiedRequest, retries) + } + + if (!isRetryable(modifiedRequest)) { + return httpClient.execute(modifiedRequest, requestOptions) + } + + val response = + try { + val response = httpClient.execute(modifiedRequest, requestOptions) + if (++retries > maxRetries || !shouldRetry(response)) { + return response + } + + response + } catch (throwable: Throwable) { + if (++retries > maxRetries || !shouldRetry(throwable)) { + throw throwable + } + + null + } + + val backoffDuration = getRetryBackoffDuration(retries, response) + // All responses must be closed, so close the failed one before retrying. + response?.close() + sleeper.sleep(backoffDuration) + } + } + + override suspend fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + var modifiedRequest = maybeAddIdempotencyHeader(request) + + // Don't send the current retry count in the headers if the caller set their own value. + val shouldSendRetryCount = + !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count") + + var retries = 0 + + while (true) { + if (shouldSendRetryCount) { + modifiedRequest = setRetryCountHeader(modifiedRequest, retries) + } + + if (!isRetryable(modifiedRequest)) { + return httpClient.executeAsync(modifiedRequest, requestOptions) + } + + val response = + try { + val response = httpClient.execute(modifiedRequest, requestOptions) + if (++retries > maxRetries || !shouldRetry(response)) { + return response + } + + response + } catch (throwable: Throwable) { + if (++retries > maxRetries || !shouldRetry(throwable)) { + throw throwable + } + + null + } + + val backoffDuration = getRetryBackoffDuration(retries, response) + // All responses must be closed, so close the failed one before retrying. + response?.close() + sleeper.sleepAsync(backoffDuration) + } + } + + override fun close() { + httpClient.close() + sleeper.close() + } + + private fun isRetryable(request: HttpRequest): Boolean = + // Some requests, such as when a request body is being streamed, cannot be retried because + // the body data aren't available on subsequent attempts. + request.body?.repeatable() ?: true + + private fun setRetryCountHeader(request: HttpRequest, retries: Int): HttpRequest = + request.toBuilder().replaceHeaders("X-Stainless-Retry-Count", retries.toString()).build() + + private fun idempotencyKey(): String = "stainless-java-retry-${UUID.randomUUID()}" + + private fun maybeAddIdempotencyHeader(request: HttpRequest): HttpRequest { + if (idempotencyHeader == null || request.headers.names().contains(idempotencyHeader)) { + return request + } + + return request + .toBuilder() + // Set a header to uniquely identify the request when retried + .putHeader(idempotencyHeader, idempotencyKey()) + .build() + } + + private fun shouldRetry(response: HttpResponse): Boolean { + // Note: this is not a standard header + val shouldRetryHeader = response.headers().values("X-Should-Retry").getOrNull(0) + val statusCode = response.statusCode() + + return when { + // If the server explicitly says whether to retry, obey + shouldRetryHeader == "true" -> true + shouldRetryHeader == "false" -> false + + // Retry on request timeouts + statusCode == 408 -> true + // Retry on lock timeouts + statusCode == 409 -> true + // Retry on rate limits + statusCode == 429 -> true + // Retry internal errors + statusCode >= 500 -> true + else -> false + } + } + + private fun shouldRetry(throwable: Throwable): Boolean = + // Only retry known retryable exceptions, other exceptions are not intended to be retried. + throwable is IOException || + throwable is GridIoException || + throwable is GridRetryableException + + private fun getRetryBackoffDuration(retries: Int, response: HttpResponse?): Duration { + // About the Retry-After header: + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + response + ?.headers() + ?.let { headers -> + headers + .values("Retry-After-Ms") + .getOrNull(0) + ?.toFloatOrNull() + ?.times(TimeUnit.MILLISECONDS.toNanos(1)) + ?: headers.values("Retry-After").getOrNull(0)?.let { retryAfter -> + retryAfter.toFloatOrNull()?.times(TimeUnit.SECONDS.toNanos(1)) + ?: try { + ChronoUnit.MILLIS.between( + OffsetDateTime.now(clock), + OffsetDateTime.parse( + retryAfter, + DateTimeFormatter.RFC_1123_DATE_TIME, + ), + ) + } catch (e: DateTimeParseException) { + null + } + } + } + ?.let { retryAfterNanos -> + // If the API asks us to wait a certain amount of time (and it's a reasonable + // amount), just + // do what it says. + val retryAfter = Duration.ofNanos(retryAfterNanos.toLong()) + if (retryAfter in Duration.ofNanos(0)..Duration.ofMinutes(1)) { + return retryAfter + } + } + + // Apply exponential backoff, but not more than the max. + val backoffSeconds = min(0.5 * 2.0.pow(retries - 1), 8.0) + + // Apply some jitter + val jitter = 1.0 - 0.25 * ThreadLocalRandom.current().nextDouble() + + return Duration.ofNanos((TimeUnit.SECONDS.toNanos(1) * backoffSeconds * jitter).toLong()) + } + + companion object { + + fun builder() = Builder() + } + + class Builder internal constructor() { + + private var httpClient: HttpClient? = null + private var sleeper: Sleeper? = null + private var clock: Clock = Clock.systemUTC() + private var maxRetries: Int = 2 + private var idempotencyHeader: String? = null + + fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient } + + fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper } + + fun clock(clock: Clock) = apply { this.clock = clock } + + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + + fun idempotencyHeader(header: String) = apply { this.idempotencyHeader = header } + + fun build(): HttpClient = + RetryingHttpClient( + checkRequired("httpClient", httpClient), + sleeper ?: DefaultSleeper(), + clock, + maxRetries, + idempotencyHeader, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/StreamResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/StreamResponse.kt new file mode 100644 index 00000000..0b4d3498 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/http/StreamResponse.kt @@ -0,0 +1,13 @@ +package com.grid.api.core.http + +interface StreamResponse : AutoCloseable { + + fun asSequence(): Sequence +} + +internal fun StreamResponse.map(transform: (T) -> R): StreamResponse = + object : StreamResponse { + override fun asSequence(): Sequence = this@map.asSequence().map(transform) + + override fun close() = this@map.close() + } diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/BadRequestException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/BadRequestException.kt new file mode 100644 index 00000000..2e023f75 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/BadRequestException.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class BadRequestException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + GridServiceException("400: $body", cause) { + + override fun statusCode(): Int = 400 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BadRequestException]. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BadRequestException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(badRequestException: BadRequestException) = apply { + headers = badRequestException.headers + body = badRequestException.body + cause = badRequestException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [BadRequestException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BadRequestException = + BadRequestException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridException.kt new file mode 100644 index 00000000..66923e09 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridException.kt @@ -0,0 +1,4 @@ +package com.grid.api.errors + +open class GridException(message: String? = null, cause: Throwable? = null) : + RuntimeException(message, cause) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridInvalidDataException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridInvalidDataException.kt new file mode 100644 index 00000000..db9785be --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridInvalidDataException.kt @@ -0,0 +1,4 @@ +package com.grid.api.errors + +class GridInvalidDataException(message: String? = null, cause: Throwable? = null) : + GridException(message, cause) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridIoException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridIoException.kt new file mode 100644 index 00000000..17385f11 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridIoException.kt @@ -0,0 +1,4 @@ +package com.grid.api.errors + +class GridIoException(message: String? = null, cause: Throwable? = null) : + GridException(message, cause) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridRetryableException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridRetryableException.kt new file mode 100644 index 00000000..b3d51dff --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridRetryableException.kt @@ -0,0 +1,13 @@ +package com.grid.api.errors + +/** + * Exception that indicates a transient error that can be retried. + * + * When this exception is thrown during an HTTP request, the SDK will automatically retry the + * request up to the maximum number of retries. + * + * @param message A descriptive error message + * @param cause The underlying cause of this exception, if any + */ +class GridRetryableException constructor(message: String? = null, cause: Throwable? = null) : + GridException(message, cause) diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridServiceException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridServiceException.kt new file mode 100644 index 00000000..35b92822 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/GridServiceException.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.http.Headers + +abstract class GridServiceException +protected constructor(message: String, cause: Throwable? = null) : GridException(message, cause) { + + abstract fun statusCode(): Int + + abstract fun headers(): Headers + + abstract fun body(): JsonValue +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/InternalServerException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/InternalServerException.kt new file mode 100644 index 00000000..b0816f92 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/InternalServerException.kt @@ -0,0 +1,85 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class InternalServerException +private constructor( + private val statusCode: Int, + private val headers: Headers, + private val body: JsonValue, + cause: Throwable?, +) : GridServiceException("$statusCode: $body", cause) { + + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InternalServerException]. + * + * The following fields are required: + * ```kotlin + * .statusCode() + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [InternalServerException]. */ + class Builder internal constructor() { + + private var statusCode: Int? = null + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(internalServerException: InternalServerException) = apply { + statusCode = internalServerException.statusCode + headers = internalServerException.headers + body = internalServerException.body + cause = internalServerException.cause + } + + fun statusCode(statusCode: Int) = apply { this.statusCode = statusCode } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [InternalServerException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .statusCode() + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InternalServerException = + InternalServerException( + checkRequired("statusCode", statusCode), + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/NotFoundException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/NotFoundException.kt new file mode 100644 index 00000000..0a787cd6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/NotFoundException.kt @@ -0,0 +1,70 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class NotFoundException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + GridServiceException("404: $body", cause) { + + override fun statusCode(): Int = 404 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [NotFoundException]. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [NotFoundException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(notFoundException: NotFoundException) = apply { + headers = notFoundException.headers + body = notFoundException.body + cause = notFoundException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [NotFoundException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): NotFoundException = + NotFoundException(checkRequired("headers", headers), checkRequired("body", body), cause) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/PermissionDeniedException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/PermissionDeniedException.kt new file mode 100644 index 00000000..4d360e71 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/PermissionDeniedException.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class PermissionDeniedException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + GridServiceException("403: $body", cause) { + + override fun statusCode(): Int = 403 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PermissionDeniedException]. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PermissionDeniedException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(permissionDeniedException: PermissionDeniedException) = apply { + headers = permissionDeniedException.headers + body = permissionDeniedException.body + cause = permissionDeniedException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [PermissionDeniedException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PermissionDeniedException = + PermissionDeniedException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/RateLimitException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/RateLimitException.kt new file mode 100644 index 00000000..b8bc105e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/RateLimitException.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class RateLimitException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + GridServiceException("429: $body", cause) { + + override fun statusCode(): Int = 429 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RateLimitException]. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [RateLimitException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(rateLimitException: RateLimitException) = apply { + headers = rateLimitException.headers + body = rateLimitException.body + cause = rateLimitException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [RateLimitException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RateLimitException = + RateLimitException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnauthorizedException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnauthorizedException.kt new file mode 100644 index 00000000..e5c02c5d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnauthorizedException.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class UnauthorizedException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + GridServiceException("401: $body", cause) { + + override fun statusCode(): Int = 401 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UnauthorizedException]. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UnauthorizedException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(unauthorizedException: UnauthorizedException) = apply { + headers = unauthorizedException.headers + body = unauthorizedException.body + cause = unauthorizedException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [UnauthorizedException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnauthorizedException = + UnauthorizedException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnexpectedStatusCodeException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnexpectedStatusCodeException.kt new file mode 100644 index 00000000..59062653 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnexpectedStatusCodeException.kt @@ -0,0 +1,86 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class UnexpectedStatusCodeException +private constructor( + private val statusCode: Int, + private val headers: Headers, + private val body: JsonValue, + cause: Throwable?, +) : GridServiceException("$statusCode: $body", cause) { + + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [UnexpectedStatusCodeException]. + * + * The following fields are required: + * ```kotlin + * .statusCode() + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UnexpectedStatusCodeException]. */ + class Builder internal constructor() { + + private var statusCode: Int? = null + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(unexpectedStatusCodeException: UnexpectedStatusCodeException) = apply { + statusCode = unexpectedStatusCodeException.statusCode + headers = unexpectedStatusCodeException.headers + body = unexpectedStatusCodeException.body + cause = unexpectedStatusCodeException.cause + } + + fun statusCode(statusCode: Int) = apply { this.statusCode = statusCode } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [UnexpectedStatusCodeException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .statusCode() + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnexpectedStatusCodeException = + UnexpectedStatusCodeException( + checkRequired("statusCode", statusCode), + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnprocessableEntityException.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnprocessableEntityException.kt new file mode 100644 index 00000000..bce99859 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/errors/UnprocessableEntityException.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.errors + +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers + +class UnprocessableEntityException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + GridServiceException("422: $body", cause) { + + override fun statusCode(): Int = 422 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UnprocessableEntityException]. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UnprocessableEntityException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + internal fun from(unprocessableEntityException: UnprocessableEntityException) = apply { + headers = unprocessableEntityException.headers + body = unprocessableEntityException.body + cause = unprocessableEntityException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** + * Returns an immutable instance of [UnprocessableEntityException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnprocessableEntityException = + UnprocessableEntityException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigRetrieveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigRetrieveParams.kt new file mode 100644 index 00000000..4f7f3f3c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigRetrieveParams.kt @@ -0,0 +1,169 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** Retrieve the current platform configuration */ +class ConfigRetrieveParams +private constructor( + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ConfigRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ConfigRetrieveParams]. */ + fun builder() = Builder() + } + + /** A builder for [ConfigRetrieveParams]. */ + class Builder internal constructor() { + + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(configRetrieveParams: ConfigRetrieveParams) = apply { + additionalHeaders = configRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = configRetrieveParams.additionalQueryParams.toBuilder() + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ConfigRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ConfigRetrieveParams = + ConfigRetrieveParams(additionalHeaders.build(), additionalQueryParams.build()) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ConfigRetrieveParams && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(additionalHeaders, additionalQueryParams) + + override fun toString() = + "ConfigRetrieveParams{additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigUpdateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigUpdateParams.kt new file mode 100644 index 00000000..bb9b54b6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/ConfigUpdateParams.kt @@ -0,0 +1,555 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkKnown +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** Update the platform configuration settings */ +class ConfigUpdateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun supportedCurrencies(): List? = body.supportedCurrencies() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun umaDomain(): String? = body.umaDomain() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun webhookEndpoint(): String? = body.webhookEndpoint() + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _supportedCurrencies(): JsonField> = + body._supportedCurrencies() + + /** + * Returns the raw JSON value of [umaDomain]. + * + * Unlike [umaDomain], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _umaDomain(): JsonField = body._umaDomain() + + /** + * Returns the raw JSON value of [webhookEndpoint]. + * + * Unlike [webhookEndpoint], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _webhookEndpoint(): JsonField = body._webhookEndpoint() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ConfigUpdateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ConfigUpdateParams]. */ + fun builder() = Builder() + } + + /** A builder for [ConfigUpdateParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(configUpdateParams: ConfigUpdateParams) = apply { + body = configUpdateParams.body.toBuilder() + additionalHeaders = configUpdateParams.additionalHeaders.toBuilder() + additionalQueryParams = configUpdateParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [supportedCurrencies] + * - [umaDomain] + * - [webhookEndpoint] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + fun supportedCurrencies(supportedCurrencies: List) = apply { + body.supportedCurrencies(supportedCurrencies) + } + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun supportedCurrencies(supportedCurrencies: JsonField>) = + apply { + body.supportedCurrencies(supportedCurrencies) + } + + /** + * Adds a single [PlatformCurrencyConfig] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: PlatformCurrencyConfig) = apply { + body.addSupportedCurrency(supportedCurrency) + } + + fun umaDomain(umaDomain: String) = apply { body.umaDomain(umaDomain) } + + /** + * Sets [Builder.umaDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.umaDomain] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun umaDomain(umaDomain: JsonField) = apply { body.umaDomain(umaDomain) } + + fun webhookEndpoint(webhookEndpoint: String) = apply { + body.webhookEndpoint(webhookEndpoint) + } + + /** + * Sets [Builder.webhookEndpoint] to an arbitrary JSON value. + * + * You should usually call [Builder.webhookEndpoint] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun webhookEndpoint(webhookEndpoint: JsonField) = apply { + body.webhookEndpoint(webhookEndpoint) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ConfigUpdateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ConfigUpdateParams = + ConfigUpdateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val supportedCurrencies: JsonField>, + private val umaDomain: JsonField, + private val webhookEndpoint: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("supportedCurrencies") + @ExcludeMissing + supportedCurrencies: JsonField> = JsonMissing.of(), + @JsonProperty("umaDomain") + @ExcludeMissing + umaDomain: JsonField = JsonMissing.of(), + @JsonProperty("webhookEndpoint") + @ExcludeMissing + webhookEndpoint: JsonField = JsonMissing.of(), + ) : this(supportedCurrencies, umaDomain, webhookEndpoint, mutableMapOf()) + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun supportedCurrencies(): List? = + supportedCurrencies.getNullable("supportedCurrencies") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun umaDomain(): String? = umaDomain.getNullable("umaDomain") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun webhookEndpoint(): String? = webhookEndpoint.getNullable("webhookEndpoint") + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("supportedCurrencies") + @ExcludeMissing + fun _supportedCurrencies(): JsonField> = supportedCurrencies + + /** + * Returns the raw JSON value of [umaDomain]. + * + * Unlike [umaDomain], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaDomain") @ExcludeMissing fun _umaDomain(): JsonField = umaDomain + + /** + * Returns the raw JSON value of [webhookEndpoint]. + * + * Unlike [webhookEndpoint], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("webhookEndpoint") + @ExcludeMissing + fun _webhookEndpoint(): JsonField = webhookEndpoint + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Body]. */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var supportedCurrencies: JsonField>? = null + private var umaDomain: JsonField = JsonMissing.of() + private var webhookEndpoint: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + supportedCurrencies = body.supportedCurrencies.map { it.toMutableList() } + umaDomain = body.umaDomain + webhookEndpoint = body.webhookEndpoint + additionalProperties = body.additionalProperties.toMutableMap() + } + + fun supportedCurrencies(supportedCurrencies: List) = + supportedCurrencies(JsonField.of(supportedCurrencies)) + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun supportedCurrencies(supportedCurrencies: JsonField>) = + apply { + this.supportedCurrencies = supportedCurrencies.map { it.toMutableList() } + } + + /** + * Adds a single [PlatformCurrencyConfig] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: PlatformCurrencyConfig) = apply { + supportedCurrencies = + (supportedCurrencies ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedCurrencies", it).add(supportedCurrency) + } + } + + fun umaDomain(umaDomain: String) = umaDomain(JsonField.of(umaDomain)) + + /** + * Sets [Builder.umaDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.umaDomain] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaDomain(umaDomain: JsonField) = apply { this.umaDomain = umaDomain } + + fun webhookEndpoint(webhookEndpoint: String) = + webhookEndpoint(JsonField.of(webhookEndpoint)) + + /** + * Sets [Builder.webhookEndpoint] to an arbitrary JSON value. + * + * You should usually call [Builder.webhookEndpoint] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun webhookEndpoint(webhookEndpoint: JsonField) = apply { + this.webhookEndpoint = webhookEndpoint + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Body = + Body( + (supportedCurrencies ?: JsonMissing.of()).map { it.toImmutable() }, + umaDomain, + webhookEndpoint, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + supportedCurrencies()?.forEach { it.validate() } + umaDomain() + webhookEndpoint() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (supportedCurrencies.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (umaDomain.asKnown() == null) 0 else 1) + + (if (webhookEndpoint.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + supportedCurrencies == other.supportedCurrencies && + umaDomain == other.umaDomain && + webhookEndpoint == other.webhookEndpoint && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(supportedCurrencies, umaDomain, webhookEndpoint, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{supportedCurrencies=$supportedCurrencies, umaDomain=$umaDomain, webhookEndpoint=$webhookEndpoint, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ConfigUpdateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ConfigUpdateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/CustomerInfoFieldName.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/CustomerInfoFieldName.kt new file mode 100644 index 00000000..668de957 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/CustomerInfoFieldName.kt @@ -0,0 +1,219 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.errors.GridInvalidDataException + +/** Name of a type of field containing info about a platform's customer or counterparty customer. */ +class CustomerInfoFieldName @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val FULL_NAME = of("FULL_NAME") + + val BIRTH_DATE = of("BIRTH_DATE") + + val NATIONALITY = of("NATIONALITY") + + val PHONE_NUMBER = of("PHONE_NUMBER") + + val EMAIL = of("EMAIL") + + val POSTAL_ADDRESS = of("POSTAL_ADDRESS") + + val TAX_ID = of("TAX_ID") + + val REGISTRATION_NUMBER = of("REGISTRATION_NUMBER") + + val USER_TYPE = of("USER_TYPE") + + val COUNTRY_OF_RESIDENCE = of("COUNTRY_OF_RESIDENCE") + + val ACCOUNT_IDENTIFIER = of("ACCOUNT_IDENTIFIER") + + val FI_LEGAL_ENTITY_NAME = of("FI_LEGAL_ENTITY_NAME") + + val FI_ADDRESS = of("FI_ADDRESS") + + val PURPOSE_OF_PAYMENT = of("PURPOSE_OF_PAYMENT") + + val ULTIMATE_INSTITUTION_COUNTRY = of("ULTIMATE_INSTITUTION_COUNTRY") + + val IDENTIFIER = of("IDENTIFIER") + + fun of(value: String) = CustomerInfoFieldName(JsonField.of(value)) + } + + /** An enum containing [CustomerInfoFieldName]'s known values. */ + enum class Known { + FULL_NAME, + BIRTH_DATE, + NATIONALITY, + PHONE_NUMBER, + EMAIL, + POSTAL_ADDRESS, + TAX_ID, + REGISTRATION_NUMBER, + USER_TYPE, + COUNTRY_OF_RESIDENCE, + ACCOUNT_IDENTIFIER, + FI_LEGAL_ENTITY_NAME, + FI_ADDRESS, + PURPOSE_OF_PAYMENT, + ULTIMATE_INSTITUTION_COUNTRY, + IDENTIFIER, + } + + /** + * An enum containing [CustomerInfoFieldName]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerInfoFieldName] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + FULL_NAME, + BIRTH_DATE, + NATIONALITY, + PHONE_NUMBER, + EMAIL, + POSTAL_ADDRESS, + TAX_ID, + REGISTRATION_NUMBER, + USER_TYPE, + COUNTRY_OF_RESIDENCE, + ACCOUNT_IDENTIFIER, + FI_LEGAL_ENTITY_NAME, + FI_ADDRESS, + PURPOSE_OF_PAYMENT, + ULTIMATE_INSTITUTION_COUNTRY, + IDENTIFIER, + /** + * An enum member indicating that [CustomerInfoFieldName] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + FULL_NAME -> Value.FULL_NAME + BIRTH_DATE -> Value.BIRTH_DATE + NATIONALITY -> Value.NATIONALITY + PHONE_NUMBER -> Value.PHONE_NUMBER + EMAIL -> Value.EMAIL + POSTAL_ADDRESS -> Value.POSTAL_ADDRESS + TAX_ID -> Value.TAX_ID + REGISTRATION_NUMBER -> Value.REGISTRATION_NUMBER + USER_TYPE -> Value.USER_TYPE + COUNTRY_OF_RESIDENCE -> Value.COUNTRY_OF_RESIDENCE + ACCOUNT_IDENTIFIER -> Value.ACCOUNT_IDENTIFIER + FI_LEGAL_ENTITY_NAME -> Value.FI_LEGAL_ENTITY_NAME + FI_ADDRESS -> Value.FI_ADDRESS + PURPOSE_OF_PAYMENT -> Value.PURPOSE_OF_PAYMENT + ULTIMATE_INSTITUTION_COUNTRY -> Value.ULTIMATE_INSTITUTION_COUNTRY + IDENTIFIER -> Value.IDENTIFIER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + FULL_NAME -> Known.FULL_NAME + BIRTH_DATE -> Known.BIRTH_DATE + NATIONALITY -> Known.NATIONALITY + PHONE_NUMBER -> Known.PHONE_NUMBER + EMAIL -> Known.EMAIL + POSTAL_ADDRESS -> Known.POSTAL_ADDRESS + TAX_ID -> Known.TAX_ID + REGISTRATION_NUMBER -> Known.REGISTRATION_NUMBER + USER_TYPE -> Known.USER_TYPE + COUNTRY_OF_RESIDENCE -> Known.COUNTRY_OF_RESIDENCE + ACCOUNT_IDENTIFIER -> Known.ACCOUNT_IDENTIFIER + FI_LEGAL_ENTITY_NAME -> Known.FI_LEGAL_ENTITY_NAME + FI_ADDRESS -> Known.FI_ADDRESS + PURPOSE_OF_PAYMENT -> Known.PURPOSE_OF_PAYMENT + ULTIMATE_INSTITUTION_COUNTRY -> Known.ULTIMATE_INSTITUTION_COUNTRY + IDENTIFIER -> Known.IDENTIFIER + else -> throw GridInvalidDataException("Unknown CustomerInfoFieldName: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerInfoFieldName = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerInfoFieldName && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformConfig.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformConfig.kt new file mode 100644 index 00000000..cc9c5de7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformConfig.kt @@ -0,0 +1,493 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class PlatformConfig +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val createdAt: JsonField, + private val isRegulatedFinancialInstitution: JsonField, + private val proxyUmaSubdomain: JsonField, + private val supportedCurrencies: JsonField>, + private val umaDomain: JsonField, + private val updatedAt: JsonField, + private val webhookEndpoint: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isRegulatedFinancialInstitution") + @ExcludeMissing + isRegulatedFinancialInstitution: JsonField = JsonMissing.of(), + @JsonProperty("proxyUmaSubdomain") + @ExcludeMissing + proxyUmaSubdomain: JsonField = JsonMissing.of(), + @JsonProperty("supportedCurrencies") + @ExcludeMissing + supportedCurrencies: JsonField> = JsonMissing.of(), + @JsonProperty("umaDomain") @ExcludeMissing umaDomain: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("webhookEndpoint") + @ExcludeMissing + webhookEndpoint: JsonField = JsonMissing.of(), + ) : this( + id, + createdAt, + isRegulatedFinancialInstitution, + proxyUmaSubdomain, + supportedCurrencies, + umaDomain, + updatedAt, + webhookEndpoint, + mutableMapOf(), + ) + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the platform is a regulated financial institution. This is used to determine if the + * platform's customers must be KYC/KYB'd by Lightspark via the KYC link flow. This can only be + * set by Lightspark during platform creation. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun isRegulatedFinancialInstitution(): Boolean? = + isRegulatedFinancialInstitution.getNullable("isRegulatedFinancialInstitution") + + /** + * The subdomain that incoming requests will be proxied to + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun proxyUmaSubdomain(): String? = proxyUmaSubdomain.getNullable("proxyUmaSubdomain") + + /** + * List of currencies supported by the platform. This is what the platform's customers are able + * to hold, send, and receive. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun supportedCurrencies(): List? = + supportedCurrencies.getNullable("supportedCurrencies") + + /** + * UMA domain for this platform + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun umaDomain(): String? = umaDomain.getNullable("umaDomain") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * URL where webhook notifications will be sent + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun webhookEndpoint(): String? = webhookEndpoint.getNullable("webhookEndpoint") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isRegulatedFinancialInstitution]. + * + * Unlike [isRegulatedFinancialInstitution], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("isRegulatedFinancialInstitution") + @ExcludeMissing + fun _isRegulatedFinancialInstitution(): JsonField = isRegulatedFinancialInstitution + + /** + * Returns the raw JSON value of [proxyUmaSubdomain]. + * + * Unlike [proxyUmaSubdomain], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("proxyUmaSubdomain") + @ExcludeMissing + fun _proxyUmaSubdomain(): JsonField = proxyUmaSubdomain + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("supportedCurrencies") + @ExcludeMissing + fun _supportedCurrencies(): JsonField> = supportedCurrencies + + /** + * Returns the raw JSON value of [umaDomain]. + * + * Unlike [umaDomain], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaDomain") @ExcludeMissing fun _umaDomain(): JsonField = umaDomain + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [webhookEndpoint]. + * + * Unlike [webhookEndpoint], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("webhookEndpoint") + @ExcludeMissing + fun _webhookEndpoint(): JsonField = webhookEndpoint + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [PlatformConfig]. */ + fun builder() = Builder() + } + + /** A builder for [PlatformConfig]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isRegulatedFinancialInstitution: JsonField = JsonMissing.of() + private var proxyUmaSubdomain: JsonField = JsonMissing.of() + private var supportedCurrencies: JsonField>? = null + private var umaDomain: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var webhookEndpoint: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(platformConfig: PlatformConfig) = apply { + id = platformConfig.id + createdAt = platformConfig.createdAt + isRegulatedFinancialInstitution = platformConfig.isRegulatedFinancialInstitution + proxyUmaSubdomain = platformConfig.proxyUmaSubdomain + supportedCurrencies = platformConfig.supportedCurrencies.map { it.toMutableList() } + umaDomain = platformConfig.umaDomain + updatedAt = platformConfig.updatedAt + webhookEndpoint = platformConfig.webhookEndpoint + additionalProperties = platformConfig.additionalProperties.toMutableMap() + } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** + * Whether the platform is a regulated financial institution. This is used to determine if + * the platform's customers must be KYC/KYB'd by Lightspark via the KYC link flow. This can + * only be set by Lightspark during platform creation. + */ + fun isRegulatedFinancialInstitution(isRegulatedFinancialInstitution: Boolean) = + isRegulatedFinancialInstitution(JsonField.of(isRegulatedFinancialInstitution)) + + /** + * Sets [Builder.isRegulatedFinancialInstitution] to an arbitrary JSON value. + * + * You should usually call [Builder.isRegulatedFinancialInstitution] with a well-typed + * [Boolean] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun isRegulatedFinancialInstitution(isRegulatedFinancialInstitution: JsonField) = + apply { + this.isRegulatedFinancialInstitution = isRegulatedFinancialInstitution + } + + /** The subdomain that incoming requests will be proxied to */ + fun proxyUmaSubdomain(proxyUmaSubdomain: String) = + proxyUmaSubdomain(JsonField.of(proxyUmaSubdomain)) + + /** + * Sets [Builder.proxyUmaSubdomain] to an arbitrary JSON value. + * + * You should usually call [Builder.proxyUmaSubdomain] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun proxyUmaSubdomain(proxyUmaSubdomain: JsonField) = apply { + this.proxyUmaSubdomain = proxyUmaSubdomain + } + + /** + * List of currencies supported by the platform. This is what the platform's customers are + * able to hold, send, and receive. + */ + fun supportedCurrencies(supportedCurrencies: List) = + supportedCurrencies(JsonField.of(supportedCurrencies)) + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun supportedCurrencies(supportedCurrencies: JsonField>) = + apply { + this.supportedCurrencies = supportedCurrencies.map { it.toMutableList() } + } + + /** + * Adds a single [PlatformCurrencyConfig] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: PlatformCurrencyConfig) = apply { + supportedCurrencies = + (supportedCurrencies ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedCurrencies", it).add(supportedCurrency) + } + } + + /** UMA domain for this platform */ + fun umaDomain(umaDomain: String) = umaDomain(JsonField.of(umaDomain)) + + /** + * Sets [Builder.umaDomain] to an arbitrary JSON value. + * + * You should usually call [Builder.umaDomain] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun umaDomain(umaDomain: JsonField) = apply { this.umaDomain = umaDomain } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + /** URL where webhook notifications will be sent */ + fun webhookEndpoint(webhookEndpoint: String) = + webhookEndpoint(JsonField.of(webhookEndpoint)) + + /** + * Sets [Builder.webhookEndpoint] to an arbitrary JSON value. + * + * You should usually call [Builder.webhookEndpoint] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun webhookEndpoint(webhookEndpoint: JsonField) = apply { + this.webhookEndpoint = webhookEndpoint + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PlatformConfig]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): PlatformConfig = + PlatformConfig( + id, + createdAt, + isRegulatedFinancialInstitution, + proxyUmaSubdomain, + (supportedCurrencies ?: JsonMissing.of()).map { it.toImmutable() }, + umaDomain, + updatedAt, + webhookEndpoint, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PlatformConfig = apply { + if (validated) { + return@apply + } + + id() + createdAt() + isRegulatedFinancialInstitution() + proxyUmaSubdomain() + supportedCurrencies()?.forEach { it.validate() } + umaDomain() + updatedAt() + webhookEndpoint() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isRegulatedFinancialInstitution.asKnown() == null) 0 else 1) + + (if (proxyUmaSubdomain.asKnown() == null) 0 else 1) + + (supportedCurrencies.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (umaDomain.asKnown() == null) 0 else 1) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (if (webhookEndpoint.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlatformConfig && + id == other.id && + createdAt == other.createdAt && + isRegulatedFinancialInstitution == other.isRegulatedFinancialInstitution && + proxyUmaSubdomain == other.proxyUmaSubdomain && + supportedCurrencies == other.supportedCurrencies && + umaDomain == other.umaDomain && + updatedAt == other.updatedAt && + webhookEndpoint == other.webhookEndpoint && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + createdAt, + isRegulatedFinancialInstitution, + proxyUmaSubdomain, + supportedCurrencies, + umaDomain, + updatedAt, + webhookEndpoint, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PlatformConfig{id=$id, createdAt=$createdAt, isRegulatedFinancialInstitution=$isRegulatedFinancialInstitution, proxyUmaSubdomain=$proxyUmaSubdomain, supportedCurrencies=$supportedCurrencies, umaDomain=$umaDomain, updatedAt=$updatedAt, webhookEndpoint=$webhookEndpoint, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformCurrencyConfig.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformCurrencyConfig.kt new file mode 100644 index 00000000..e2d9df41 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/config/PlatformCurrencyConfig.kt @@ -0,0 +1,570 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.receiver.CounterpartyFieldDefinition +import com.grid.api.models.transactions.TransactionType +import java.util.Collections +import java.util.Objects + +class PlatformCurrencyConfig +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val currencyCode: JsonField, + private val enabledTransactionTypes: JsonField>, + private val maxAmount: JsonField, + private val minAmount: JsonField, + private val requiredCounterpartyFields: JsonField>, + private val providerRequiredCounterpartyCustomerFields: JsonField>, + private val providerRequiredCustomerFields: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("currencyCode") + @ExcludeMissing + currencyCode: JsonField = JsonMissing.of(), + @JsonProperty("enabledTransactionTypes") + @ExcludeMissing + enabledTransactionTypes: JsonField> = JsonMissing.of(), + @JsonProperty("maxAmount") @ExcludeMissing maxAmount: JsonField = JsonMissing.of(), + @JsonProperty("minAmount") @ExcludeMissing minAmount: JsonField = JsonMissing.of(), + @JsonProperty("requiredCounterpartyFields") + @ExcludeMissing + requiredCounterpartyFields: JsonField> = JsonMissing.of(), + @JsonProperty("providerRequiredCounterpartyCustomerFields") + @ExcludeMissing + providerRequiredCounterpartyCustomerFields: JsonField> = + JsonMissing.of(), + @JsonProperty("providerRequiredCustomerFields") + @ExcludeMissing + providerRequiredCustomerFields: JsonField> = JsonMissing.of(), + ) : this( + currencyCode, + enabledTransactionTypes, + maxAmount, + minAmount, + requiredCounterpartyFields, + providerRequiredCounterpartyCustomerFields, + providerRequiredCustomerFields, + mutableMapOf(), + ) + + /** + * Three-letter currency code (ISO 4217) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun currencyCode(): String = currencyCode.getRequired("currencyCode") + + /** + * List of transaction types that are enabled for this currency. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun enabledTransactionTypes(): List = + enabledTransactionTypes.getRequired("enabledTransactionTypes") + + /** + * Maximum amount that can be sent in the smallest unit of this currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun maxAmount(): Long = maxAmount.getRequired("maxAmount") + + /** + * Minimum amount that can be sent in the smallest unit of this currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun minAmount(): Long = minAmount.getRequired("minAmount") + + /** + * List of fields which the platform requires from the counterparty institutions about + * counterparty customers. Platforms can set mandatory to false if the platform does not require + * the field, but would like to have it available. Some fields may be required by the underlying + * UMA provider. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun requiredCounterpartyFields(): List = + requiredCounterpartyFields.getRequired("requiredCounterpartyFields") + + /** + * List of fields that are required by the underlying UMA provider for this currency. If the + * counterparty does not provide these fields, quote requests will fail. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun providerRequiredCounterpartyCustomerFields(): List? = + providerRequiredCounterpartyCustomerFields.getNullable( + "providerRequiredCounterpartyCustomerFields" + ) + + /** + * List of customer info field names that are required by the underlying UMA provider when + * creating a customer for this currency. These fields must be supplied when creating or + * updating a customer if this currency is intended to be used by that customer. If no fields + * are required, this field is omitted. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun providerRequiredCustomerFields(): List? = + providerRequiredCustomerFields.getNullable("providerRequiredCustomerFields") + + /** + * Returns the raw JSON value of [currencyCode]. + * + * Unlike [currencyCode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currencyCode") + @ExcludeMissing + fun _currencyCode(): JsonField = currencyCode + + /** + * Returns the raw JSON value of [enabledTransactionTypes]. + * + * Unlike [enabledTransactionTypes], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("enabledTransactionTypes") + @ExcludeMissing + fun _enabledTransactionTypes(): JsonField> = enabledTransactionTypes + + /** + * Returns the raw JSON value of [maxAmount]. + * + * Unlike [maxAmount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("maxAmount") @ExcludeMissing fun _maxAmount(): JsonField = maxAmount + + /** + * Returns the raw JSON value of [minAmount]. + * + * Unlike [minAmount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("minAmount") @ExcludeMissing fun _minAmount(): JsonField = minAmount + + /** + * Returns the raw JSON value of [requiredCounterpartyFields]. + * + * Unlike [requiredCounterpartyFields], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("requiredCounterpartyFields") + @ExcludeMissing + fun _requiredCounterpartyFields(): JsonField> = + requiredCounterpartyFields + + /** + * Returns the raw JSON value of [providerRequiredCounterpartyCustomerFields]. + * + * Unlike [providerRequiredCounterpartyCustomerFields], this method doesn't throw if the JSON + * field has an unexpected type. + */ + @JsonProperty("providerRequiredCounterpartyCustomerFields") + @ExcludeMissing + fun _providerRequiredCounterpartyCustomerFields(): JsonField> = + providerRequiredCounterpartyCustomerFields + + /** + * Returns the raw JSON value of [providerRequiredCustomerFields]. + * + * Unlike [providerRequiredCustomerFields], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("providerRequiredCustomerFields") + @ExcludeMissing + fun _providerRequiredCustomerFields(): JsonField> = + providerRequiredCustomerFields + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PlatformCurrencyConfig]. + * + * The following fields are required: + * ```kotlin + * .currencyCode() + * .enabledTransactionTypes() + * .maxAmount() + * .minAmount() + * .requiredCounterpartyFields() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PlatformCurrencyConfig]. */ + class Builder internal constructor() { + + private var currencyCode: JsonField? = null + private var enabledTransactionTypes: JsonField>? = null + private var maxAmount: JsonField? = null + private var minAmount: JsonField? = null + private var requiredCounterpartyFields: + JsonField>? = + null + private var providerRequiredCounterpartyCustomerFields: + JsonField>? = + null + private var providerRequiredCustomerFields: JsonField>? = + null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(platformCurrencyConfig: PlatformCurrencyConfig) = apply { + currencyCode = platformCurrencyConfig.currencyCode + enabledTransactionTypes = + platformCurrencyConfig.enabledTransactionTypes.map { it.toMutableList() } + maxAmount = platformCurrencyConfig.maxAmount + minAmount = platformCurrencyConfig.minAmount + requiredCounterpartyFields = + platformCurrencyConfig.requiredCounterpartyFields.map { it.toMutableList() } + providerRequiredCounterpartyCustomerFields = + platformCurrencyConfig.providerRequiredCounterpartyCustomerFields.map { + it.toMutableList() + } + providerRequiredCustomerFields = + platformCurrencyConfig.providerRequiredCustomerFields.map { it.toMutableList() } + additionalProperties = platformCurrencyConfig.additionalProperties.toMutableMap() + } + + /** Three-letter currency code (ISO 4217) */ + fun currencyCode(currencyCode: String) = currencyCode(JsonField.of(currencyCode)) + + /** + * Sets [Builder.currencyCode] to an arbitrary JSON value. + * + * You should usually call [Builder.currencyCode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun currencyCode(currencyCode: JsonField) = apply { + this.currencyCode = currencyCode + } + + /** List of transaction types that are enabled for this currency. */ + fun enabledTransactionTypes(enabledTransactionTypes: List) = + enabledTransactionTypes(JsonField.of(enabledTransactionTypes)) + + /** + * Sets [Builder.enabledTransactionTypes] to an arbitrary JSON value. + * + * You should usually call [Builder.enabledTransactionTypes] with a well-typed + * `List` value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun enabledTransactionTypes(enabledTransactionTypes: JsonField>) = + apply { + this.enabledTransactionTypes = enabledTransactionTypes.map { it.toMutableList() } + } + + /** + * Adds a single [TransactionType] to [enabledTransactionTypes]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addEnabledTransactionType(enabledTransactionType: TransactionType) = apply { + enabledTransactionTypes = + (enabledTransactionTypes ?: JsonField.of(mutableListOf())).also { + checkKnown("enabledTransactionTypes", it).add(enabledTransactionType) + } + } + + /** Maximum amount that can be sent in the smallest unit of this currency */ + fun maxAmount(maxAmount: Long) = maxAmount(JsonField.of(maxAmount)) + + /** + * Sets [Builder.maxAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.maxAmount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun maxAmount(maxAmount: JsonField) = apply { this.maxAmount = maxAmount } + + /** Minimum amount that can be sent in the smallest unit of this currency */ + fun minAmount(minAmount: Long) = minAmount(JsonField.of(minAmount)) + + /** + * Sets [Builder.minAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.minAmount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun minAmount(minAmount: JsonField) = apply { this.minAmount = minAmount } + + /** + * List of fields which the platform requires from the counterparty institutions about + * counterparty customers. Platforms can set mandatory to false if the platform does not + * require the field, but would like to have it available. Some fields may be required by + * the underlying UMA provider. + */ + fun requiredCounterpartyFields( + requiredCounterpartyFields: List + ) = requiredCounterpartyFields(JsonField.of(requiredCounterpartyFields)) + + /** + * Sets [Builder.requiredCounterpartyFields] to an arbitrary JSON value. + * + * You should usually call [Builder.requiredCounterpartyFields] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun requiredCounterpartyFields( + requiredCounterpartyFields: JsonField> + ) = apply { + this.requiredCounterpartyFields = requiredCounterpartyFields.map { it.toMutableList() } + } + + /** + * Adds a single [CounterpartyFieldDefinition] to [requiredCounterpartyFields]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRequiredCounterpartyField(requiredCounterpartyField: CounterpartyFieldDefinition) = + apply { + requiredCounterpartyFields = + (requiredCounterpartyFields ?: JsonField.of(mutableListOf())).also { + checkKnown("requiredCounterpartyFields", it).add(requiredCounterpartyField) + } + } + + /** + * List of fields that are required by the underlying UMA provider for this currency. If the + * counterparty does not provide these fields, quote requests will fail. + */ + fun providerRequiredCounterpartyCustomerFields( + providerRequiredCounterpartyCustomerFields: List + ) = + providerRequiredCounterpartyCustomerFields( + JsonField.of(providerRequiredCounterpartyCustomerFields) + ) + + /** + * Sets [Builder.providerRequiredCounterpartyCustomerFields] to an arbitrary JSON value. + * + * You should usually call [Builder.providerRequiredCounterpartyCustomerFields] with a + * well-typed `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun providerRequiredCounterpartyCustomerFields( + providerRequiredCounterpartyCustomerFields: JsonField> + ) = apply { + this.providerRequiredCounterpartyCustomerFields = + providerRequiredCounterpartyCustomerFields.map { it.toMutableList() } + } + + /** + * Adds a single [CustomerInfoFieldName] to [providerRequiredCounterpartyCustomerFields]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addProviderRequiredCounterpartyCustomerField( + providerRequiredCounterpartyCustomerField: CustomerInfoFieldName + ) = apply { + providerRequiredCounterpartyCustomerFields = + (providerRequiredCounterpartyCustomerFields ?: JsonField.of(mutableListOf())).also { + checkKnown("providerRequiredCounterpartyCustomerFields", it) + .add(providerRequiredCounterpartyCustomerField) + } + } + + /** + * List of customer info field names that are required by the underlying UMA provider when + * creating a customer for this currency. These fields must be supplied when creating or + * updating a customer if this currency is intended to be used by that customer. If no + * fields are required, this field is omitted. + */ + fun providerRequiredCustomerFields( + providerRequiredCustomerFields: List + ) = providerRequiredCustomerFields(JsonField.of(providerRequiredCustomerFields)) + + /** + * Sets [Builder.providerRequiredCustomerFields] to an arbitrary JSON value. + * + * You should usually call [Builder.providerRequiredCustomerFields] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun providerRequiredCustomerFields( + providerRequiredCustomerFields: JsonField> + ) = apply { + this.providerRequiredCustomerFields = + providerRequiredCustomerFields.map { it.toMutableList() } + } + + /** + * Adds a single [CustomerInfoFieldName] to [providerRequiredCustomerFields]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addProviderRequiredCustomerField(providerRequiredCustomerField: CustomerInfoFieldName) = + apply { + providerRequiredCustomerFields = + (providerRequiredCustomerFields ?: JsonField.of(mutableListOf())).also { + checkKnown("providerRequiredCustomerFields", it) + .add(providerRequiredCustomerField) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PlatformCurrencyConfig]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .currencyCode() + * .enabledTransactionTypes() + * .maxAmount() + * .minAmount() + * .requiredCounterpartyFields() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PlatformCurrencyConfig = + PlatformCurrencyConfig( + checkRequired("currencyCode", currencyCode), + checkRequired("enabledTransactionTypes", enabledTransactionTypes).map { + it.toImmutable() + }, + checkRequired("maxAmount", maxAmount), + checkRequired("minAmount", minAmount), + checkRequired("requiredCounterpartyFields", requiredCounterpartyFields).map { + it.toImmutable() + }, + (providerRequiredCounterpartyCustomerFields ?: JsonMissing.of()).map { + it.toImmutable() + }, + (providerRequiredCustomerFields ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PlatformCurrencyConfig = apply { + if (validated) { + return@apply + } + + currencyCode() + enabledTransactionTypes().forEach { it.validate() } + maxAmount() + minAmount() + requiredCounterpartyFields().forEach { it.validate() } + providerRequiredCounterpartyCustomerFields()?.forEach { it.validate() } + providerRequiredCustomerFields()?.forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currencyCode.asKnown() == null) 0 else 1) + + (enabledTransactionTypes.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (maxAmount.asKnown() == null) 0 else 1) + + (if (minAmount.asKnown() == null) 0 else 1) + + (requiredCounterpartyFields.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (providerRequiredCounterpartyCustomerFields.asKnown()?.sumOf { it.validity().toInt() } + ?: 0) + + (providerRequiredCustomerFields.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlatformCurrencyConfig && + currencyCode == other.currencyCode && + enabledTransactionTypes == other.enabledTransactionTypes && + maxAmount == other.maxAmount && + minAmount == other.minAmount && + requiredCounterpartyFields == other.requiredCounterpartyFields && + providerRequiredCounterpartyCustomerFields == + other.providerRequiredCounterpartyCustomerFields && + providerRequiredCustomerFields == other.providerRequiredCustomerFields && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + currencyCode, + enabledTransactionTypes, + maxAmount, + minAmount, + requiredCounterpartyFields, + providerRequiredCounterpartyCustomerFields, + providerRequiredCustomerFields, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PlatformCurrencyConfig{currencyCode=$currencyCode, enabledTransactionTypes=$enabledTransactionTypes, maxAmount=$maxAmount, minAmount=$minAmount, requiredCounterpartyFields=$requiredCounterpartyFields, providerRequiredCounterpartyCustomerFields=$providerRequiredCounterpartyCustomerFields, providerRequiredCustomerFields=$providerRequiredCustomerFields, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/Customer.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/Customer.kt new file mode 100644 index 00000000..fb5ea6d2 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/Customer.kt @@ -0,0 +1,588 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class Customer +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") @ExcludeMissing isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + mutableMapOf(), + ) + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an optional + * identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun kycStatus(): KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") @ExcludeMissing fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") @ExcludeMissing fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Customer]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Customer]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(customer: Customer) = apply { + platformCustomerId = customer.platformCustomerId + umaAddress = customer.umaAddress + id = customer.id + createdAt = customer.createdAt + isDeleted = customer.isDeleted + kycStatus = customer.kycStatus + updatedAt = customer.updatedAt + additionalProperties = customer.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [KycStatus] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun kycStatus(kycStatus: JsonField) = apply { this.kycStatus = kycStatus } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Customer]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Customer = + Customer( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Customer = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + /** The current KYC status of a customer */ + class KycStatus @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val APPROVED = of("APPROVED") + + val REJECTED = of("REJECTED") + + val PENDING_REVIEW = of("PENDING_REVIEW") + + val EXPIRED = of("EXPIRED") + + val CANCELED = of("CANCELED") + + val MANUALLY_APPROVED = of("MANUALLY_APPROVED") + + val MANUALLY_REJECTED = of("MANUALLY_REJECTED") + + fun of(value: String) = KycStatus(JsonField.of(value)) + } + + /** An enum containing [KycStatus]'s known values. */ + enum class Known { + APPROVED, + REJECTED, + PENDING_REVIEW, + EXPIRED, + CANCELED, + MANUALLY_APPROVED, + MANUALLY_REJECTED, + } + + /** + * An enum containing [KycStatus]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [KycStatus] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + APPROVED, + REJECTED, + PENDING_REVIEW, + EXPIRED, + CANCELED, + MANUALLY_APPROVED, + MANUALLY_REJECTED, + /** + * An enum member indicating that [KycStatus] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + APPROVED -> Value.APPROVED + REJECTED -> Value.REJECTED + PENDING_REVIEW -> Value.PENDING_REVIEW + EXPIRED -> Value.EXPIRED + CANCELED -> Value.CANCELED + MANUALLY_APPROVED -> Value.MANUALLY_APPROVED + MANUALLY_REJECTED -> Value.MANUALLY_REJECTED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + APPROVED -> Known.APPROVED + REJECTED -> Known.REJECTED + PENDING_REVIEW -> Known.PENDING_REVIEW + EXPIRED -> Known.EXPIRED + CANCELED -> Known.CANCELED + MANUALLY_APPROVED -> Known.MANUALLY_APPROVED + MANUALLY_REJECTED -> Known.MANUALLY_REJECTED + else -> throw GridInvalidDataException("Unknown KycStatus: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): KycStatus = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is KycStatus && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Customer && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Customer{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreate.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreate.kt new file mode 100644 index 00000000..c8e3c8df --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreate.kt @@ -0,0 +1,230 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class CustomerCreate +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") @ExcludeMissing umaAddress: JsonField = JsonMissing.of(), + ) : this(platformCustomerId, umaAddress, mutableMapOf()) + + /** + * Platform-specific customer identifier. If not provided, one will be generated by the system. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Optional UMA address identifier. If not provided during customer creation, one will be + * generated by the system. If provided during customer update, the UMA address will be updated + * to the provided value. This is an optional identifier to route payments to the customer. This + * is an optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun umaAddress(): String? = umaAddress.getNullable("umaAddress") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") @ExcludeMissing fun _umaAddress(): JsonField = umaAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerCreate]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerCreate]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(customerCreate: CustomerCreate) = apply { + platformCustomerId = customerCreate.platformCustomerId + umaAddress = customerCreate.umaAddress + additionalProperties = customerCreate.additionalProperties.toMutableMap() + } + + /** + * Platform-specific customer identifier. If not provided, one will be generated by the + * system. + */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Optional UMA address identifier. If not provided during customer creation, one will be + * generated by the system. If provided during customer update, the UMA address will be + * updated to the provided value. This is an optional identifier to route payments to the + * customer. This is an optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomerCreate]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerCreate = + CustomerCreate( + checkRequired("platformCustomerId", platformCustomerId), + umaAddress, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CustomerCreate = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerCreate && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(platformCustomerId, umaAddress, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomerCreate{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreateParams.kt new file mode 100644 index 00000000..2d3bcec2 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerCreateParams.kt @@ -0,0 +1,3692 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.util.Collections +import java.util.Objects + +/** Register a new customer in the system with an account identifier and bank account information */ +class CustomerCreateParams +private constructor( + private val createCustomerRequest: CreateCustomerRequest, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun createCustomerRequest(): CreateCustomerRequest = createCustomerRequest + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerCreateParams]. + * + * The following fields are required: + * ```kotlin + * .createCustomerRequest() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerCreateParams]. */ + class Builder internal constructor() { + + private var createCustomerRequest: CreateCustomerRequest? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(customerCreateParams: CustomerCreateParams) = apply { + createCustomerRequest = customerCreateParams.createCustomerRequest + additionalHeaders = customerCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = customerCreateParams.additionalQueryParams.toBuilder() + } + + fun createCustomerRequest(createCustomerRequest: CreateCustomerRequest) = apply { + this.createCustomerRequest = createCustomerRequest + } + + /** + * Alias for calling [createCustomerRequest] with + * `CreateCustomerRequest.ofIndividual(individual)`. + */ + fun createCustomerRequest(individual: CreateCustomerRequest.Individual) = + createCustomerRequest(CreateCustomerRequest.ofIndividual(individual)) + + /** + * Alias for calling [createCustomerRequest] with the following: + * ```kotlin + * CreateCustomerRequest.Individual.builder() + * .customerType(CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType.INDIVIDUAL) + * .platformCustomerId(platformCustomerId) + * .build() + * ``` + */ + fun individualCreateCustomerRequest(platformCustomerId: String) = + createCustomerRequest( + CreateCustomerRequest.Individual.builder() + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .platformCustomerId(platformCustomerId) + .build() + ) + + /** + * Alias for calling [createCustomerRequest] with + * `CreateCustomerRequest.ofBusiness(business)`. + */ + fun createCustomerRequest(business: CreateCustomerRequest.Business) = + createCustomerRequest(CreateCustomerRequest.ofBusiness(business)) + + /** + * Alias for calling [createCustomerRequest] with the following: + * ```kotlin + * CreateCustomerRequest.Business.builder() + * .customerType(CustomerCreateParams.CreateCustomerRequest.Business.CustomerType.BUSINESS) + * .platformCustomerId(platformCustomerId) + * .build() + * ``` + */ + fun businessCreateCustomerRequest(platformCustomerId: String) = + createCustomerRequest( + CreateCustomerRequest.Business.builder() + .customerType( + CustomerCreateParams.CreateCustomerRequest.Business.CustomerType.BUSINESS + ) + .platformCustomerId(platformCustomerId) + .build() + ) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [CustomerCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .createCustomerRequest() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerCreateParams = + CustomerCreateParams( + checkRequired("createCustomerRequest", createCustomerRequest), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): CreateCustomerRequest = createCustomerRequest + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @JsonDeserialize(using = CreateCustomerRequest.Deserializer::class) + @JsonSerialize(using = CreateCustomerRequest.Serializer::class) + class CreateCustomerRequest + private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, + ) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): CreateCustomerRequest = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CreateCustomerRequest && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "CreateCustomerRequest{individual=$individual}" + business != null -> "CreateCustomerRequest{business=$business}" + _json != null -> "CreateCustomerRequest{_unknown=$_json}" + else -> throw IllegalStateException("Invalid CreateCustomerRequest") + } + + companion object { + + fun ofIndividual(individual: Individual) = + CreateCustomerRequest(individual = individual) + + fun ofBusiness(business: Business) = CreateCustomerRequest(business = business) + } + + /** + * An interface that defines how to map each variant of [CreateCustomerRequest] to a value + * of type [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [CreateCustomerRequest] to a value of type [T]. + * + * An instance of [CreateCustomerRequest] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK + * is on an older version than the API, then the API may respond with new variants that + * the SDK is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown CreateCustomerRequest: $json") + } + } + + internal class Deserializer : + BaseDeserializer(CreateCustomerRequest::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): CreateCustomerRequest { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + CreateCustomerRequest(individual = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + CreateCustomerRequest(business = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> CreateCustomerRequest(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(CreateCustomerRequest::class) { + + override fun serialize( + value: CreateCustomerRequest, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid CreateCustomerRequest") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + customerType, + address, + birthDate, + fullName, + nationality, + mutableMapOf(), + ) + + fun toCustomerCreate(): CustomerCreate = + CustomerCreate.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .build() + + /** + * Platform-specific customer identifier. If not provided, one will be generated by the + * system. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Optional UMA address identifier. If not provided during customer creation, one will + * be generated by the system. If provided during customer update, the UMA address will + * be updated to the provided value. This is an optional identifier to route payments to + * the customer. This is an optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun umaAddress(): String? = umaAddress.getNullable("umaAddress") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun fullName(): String? = fullName.getNullable("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var fullName: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + platformCustomerId = individual.platformCustomerId + umaAddress = individual.umaAddress + customerType = individual.customerType + address = individual.address + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** + * Platform-specific customer identifier. If not provided, one will be generated by + * the system. + */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Optional UMA address identifier. If not provided during customer creation, one + * will be generated by the system. If provided during customer update, the UMA + * address will be updated to the provided value. This is an optional identifier to + * route payments to the customer. This is an optional identifier to route payments + * to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { + this.umaAddress = umaAddress + } + + fun customerType(customerType: CustomerType) = + customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + checkRequired("platformCustomerId", platformCustomerId), + umaAddress, + checkRequired("customerType", customerType), + address, + birthDate, + fullName, + nationality, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + customerType().validate() + address()?.validate() + birthDate() + fullName() + nationality() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + class CustomerType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + /** + * An enum member indicating that [CustomerType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + customerType == other.customerType && + address == other.address && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + customerType, + address, + birthDate, + fullName, + nationality, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, customerType=$customerType, address=$address, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val beneficialOwners: JsonField>, + private val businessInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("beneficialOwners") + @ExcludeMissing + beneficialOwners: JsonField> = JsonMissing.of(), + @JsonProperty("businessInfo") + @ExcludeMissing + businessInfo: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + customerType, + address, + beneficialOwners, + businessInfo, + mutableMapOf(), + ) + + fun toCustomerCreate(): CustomerCreate = + CustomerCreate.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .build() + + /** + * Platform-specific customer identifier. If not provided, one will be generated by the + * system. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Optional UMA address identifier. If not provided during customer creation, one will + * be generated by the system. If provided during customer update, the UMA address will + * be updated to the provided value. This is an optional identifier to route payments to + * the customer. This is an optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun umaAddress(): String? = umaAddress.getNullable("umaAddress") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun beneficialOwners(): List? = + beneficialOwners.getNullable("beneficialOwners") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun businessInfo(): BusinessInfo? = businessInfo.getNullable("businessInfo") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [beneficialOwners]. + * + * Unlike [beneficialOwners], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("beneficialOwners") + @ExcludeMissing + fun _beneficialOwners(): JsonField> = beneficialOwners + + /** + * Returns the raw JSON value of [businessInfo]. + * + * Unlike [businessInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("businessInfo") + @ExcludeMissing + fun _businessInfo(): JsonField = businessInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var beneficialOwners: JsonField>? = null + private var businessInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + platformCustomerId = business.platformCustomerId + umaAddress = business.umaAddress + customerType = business.customerType + address = business.address + beneficialOwners = business.beneficialOwners.map { it.toMutableList() } + businessInfo = business.businessInfo + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** + * Platform-specific customer identifier. If not provided, one will be generated by + * the system. + */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Optional UMA address identifier. If not provided during customer creation, one + * will be generated by the system. If provided during customer update, the UMA + * address will be updated to the provided value. This is an optional identifier to + * route payments to the customer. This is an optional identifier to route payments + * to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { + this.umaAddress = umaAddress + } + + fun customerType(customerType: CustomerType) = + customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun beneficialOwners(beneficialOwners: List) = + beneficialOwners(JsonField.of(beneficialOwners)) + + /** + * Sets [Builder.beneficialOwners] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficialOwners] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun beneficialOwners(beneficialOwners: JsonField>) = apply { + this.beneficialOwners = beneficialOwners.map { it.toMutableList() } + } + + /** + * Adds a single [BeneficialOwner] to [beneficialOwners]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBeneficialOwner(beneficialOwner: BeneficialOwner) = apply { + beneficialOwners = + (beneficialOwners ?: JsonField.of(mutableListOf())).also { + checkKnown("beneficialOwners", it).add(beneficialOwner) + } + } + + fun businessInfo(businessInfo: BusinessInfo) = + businessInfo(JsonField.of(businessInfo)) + + /** + * Sets [Builder.businessInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.businessInfo] with a well-typed [BusinessInfo] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun businessInfo(businessInfo: JsonField) = apply { + this.businessInfo = businessInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + checkRequired("platformCustomerId", platformCustomerId), + umaAddress, + checkRequired("customerType", customerType), + address, + (beneficialOwners ?: JsonMissing.of()).map { it.toImmutable() }, + businessInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + customerType().validate() + address()?.validate() + beneficialOwners()?.forEach { it.validate() } + businessInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (beneficialOwners.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (businessInfo.asKnown()?.validity() ?: 0) + + class CustomerType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + BUSINESS + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + class BeneficialOwner + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fullName: JsonField, + private val individualType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val emailAddress: JsonField, + private val nationality: JsonField, + private val percentageOwnership: JsonField, + private val phoneNumber: JsonField, + private val taxId: JsonField, + private val title: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("individualType") + @ExcludeMissing + individualType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("emailAddress") + @ExcludeMissing + emailAddress: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("percentageOwnership") + @ExcludeMissing + percentageOwnership: JsonField = JsonMissing.of(), + @JsonProperty("phoneNumber") + @ExcludeMissing + phoneNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") + @ExcludeMissing + taxId: JsonField = JsonMissing.of(), + @JsonProperty("title") + @ExcludeMissing + title: JsonField = JsonMissing.of(), + ) : this( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + mutableMapOf(), + ) + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Type of individual in the corporation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun individualType(): IndividualType = individualType.getRequired("individualType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Email address of the individual + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun emailAddress(): String? = emailAddress.getNullable("emailAddress") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Percent of ownership when individual type is beneficial owner + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun percentageOwnership(): Double? = + percentageOwnership.getNullable("percentageOwnership") + + /** + * Phone number of the individual in E.164 format + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun phoneNumber(): String? = phoneNumber.getNullable("phoneNumber") + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Title at company + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun title(): String? = title.getNullable("title") + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") + @ExcludeMissing + fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [individualType]. + * + * Unlike [individualType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("individualType") + @ExcludeMissing + fun _individualType(): JsonField = individualType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("address") + @ExcludeMissing + fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [emailAddress]. + * + * Unlike [emailAddress], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("emailAddress") + @ExcludeMissing + fun _emailAddress(): JsonField = emailAddress + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [percentageOwnership]. + * + * Unlike [percentageOwnership], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentageOwnership") + @ExcludeMissing + fun _percentageOwnership(): JsonField = percentageOwnership + + /** + * Returns the raw JSON value of [phoneNumber]. + * + * Unlike [phoneNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("phoneNumber") + @ExcludeMissing + fun _phoneNumber(): JsonField = phoneNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BeneficialOwner]. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BeneficialOwner]. */ + class Builder internal constructor() { + + private var fullName: JsonField? = null + private var individualType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var emailAddress: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var percentageOwnership: JsonField = JsonMissing.of() + private var phoneNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(beneficialOwner: BeneficialOwner) = apply { + fullName = beneficialOwner.fullName + individualType = beneficialOwner.individualType + address = beneficialOwner.address + birthDate = beneficialOwner.birthDate + emailAddress = beneficialOwner.emailAddress + nationality = beneficialOwner.nationality + percentageOwnership = beneficialOwner.percentageOwnership + phoneNumber = beneficialOwner.phoneNumber + taxId = beneficialOwner.taxId + title = beneficialOwner.title + additionalProperties = beneficialOwner.additionalProperties.toMutableMap() + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Type of individual in the corporation */ + fun individualType(individualType: IndividualType) = + individualType(JsonField.of(individualType)) + + /** + * Sets [Builder.individualType] to an arbitrary JSON value. + * + * You should usually call [Builder.individualType] with a well-typed + * [IndividualType] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun individualType(individualType: JsonField) = apply { + this.individualType = individualType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Email address of the individual */ + fun emailAddress(emailAddress: String) = + emailAddress(JsonField.of(emailAddress)) + + /** + * Sets [Builder.emailAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.emailAddress] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun emailAddress(emailAddress: JsonField) = apply { + this.emailAddress = emailAddress + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + /** Percent of ownership when individual type is beneficial owner */ + fun percentageOwnership(percentageOwnership: Double) = + percentageOwnership(JsonField.of(percentageOwnership)) + + /** + * Sets [Builder.percentageOwnership] to an arbitrary JSON value. + * + * You should usually call [Builder.percentageOwnership] with a well-typed + * [Double] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun percentageOwnership(percentageOwnership: JsonField) = apply { + this.percentageOwnership = percentageOwnership + } + + /** Phone number of the individual in E.164 format */ + fun phoneNumber(phoneNumber: String) = phoneNumber(JsonField.of(phoneNumber)) + + /** + * Sets [Builder.phoneNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.phoneNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun phoneNumber(phoneNumber: JsonField) = apply { + this.phoneNumber = phoneNumber + } + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + /** Title at company */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BeneficialOwner]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BeneficialOwner = + BeneficialOwner( + checkRequired("fullName", fullName), + checkRequired("individualType", individualType), + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BeneficialOwner = apply { + if (validated) { + return@apply + } + + fullName() + individualType().validate() + address()?.validate() + birthDate() + emailAddress() + nationality() + percentageOwnership() + phoneNumber() + taxId() + title() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (fullName.asKnown() == null) 0 else 1) + + (individualType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (emailAddress.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (if (percentageOwnership.asKnown() == null) 0 else 1) + + (if (phoneNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + (if (title.asKnown() == null) 0 else 1) + + /** Type of individual in the corporation */ + class IndividualType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + val DIRECTOR = of("DIRECTOR") + + val CONTROL_PERSON = of("CONTROL_PERSON") + + val BUSINESS_POINT_OF_CONTACT = of("BUSINESS_POINT_OF_CONTACT") + + val TRUSTEE = of("TRUSTEE") + + val SETTLOR = of("SETTLOR") + + val GENERAL_PARTNER = of("GENERAL_PARTNER") + + fun of(value: String) = IndividualType(JsonField.of(value)) + } + + /** An enum containing [IndividualType]'s known values. */ + enum class Known { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + } + + /** + * An enum containing [IndividualType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [IndividualType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + /** + * An enum member indicating that [IndividualType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIRECTOR -> Value.DIRECTOR + CONTROL_PERSON -> Value.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Value.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Value.TRUSTEE + SETTLOR -> Value.SETTLOR + GENERAL_PARTNER -> Value.GENERAL_PARTNER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + DIRECTOR -> Known.DIRECTOR + CONTROL_PERSON -> Known.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Known.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Known.TRUSTEE + SETTLOR -> Known.SETTLOR + GENERAL_PARTNER -> Known.GENERAL_PARTNER + else -> throw GridInvalidDataException("Unknown IndividualType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString() + ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): IndividualType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IndividualType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("country") + @ExcludeMissing + fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties( + additionalProperties: Map + ) = apply { this.additionalProperties.putAll(additionalProperties) } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficialOwner && + fullName == other.fullName && + individualType == other.individualType && + address == other.address && + birthDate == other.birthDate && + emailAddress == other.emailAddress && + nationality == other.nationality && + percentageOwnership == other.percentageOwnership && + phoneNumber == other.phoneNumber && + taxId == other.taxId && + title == other.title && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BeneficialOwner{fullName=$fullName, individualType=$individualType, address=$address, birthDate=$birthDate, emailAddress=$emailAddress, nationality=$nationality, percentageOwnership=$percentageOwnership, phoneNumber=$phoneNumber, taxId=$taxId, title=$title, additionalProperties=$additionalProperties}" + } + + class BusinessInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val legalName: JsonField, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") + @ExcludeMissing + taxId: JsonField = JsonMissing.of(), + ) : this(legalName, registrationNumber, taxId, mutableMapOf()) + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun legalName(): String = legalName.getRequired("legalName") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun registrationNumber(): String? = + registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("legalName") + @ExcludeMissing + fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BusinessInfo]. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BusinessInfo]. */ + class Builder internal constructor() { + + private var legalName: JsonField? = null + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(businessInfo: BusinessInfo) = apply { + legalName = businessInfo.legalName + registrationNumber = businessInfo.registrationNumber + taxId = businessInfo.taxId + additionalProperties = businessInfo.additionalProperties.toMutableMap() + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun legalName(legalName: JsonField) = apply { + this.legalName = legalName + } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed + * [String] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BusinessInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BusinessInfo = + BusinessInfo( + checkRequired("legalName", legalName), + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BusinessInfo = apply { + if (validated) { + return@apply + } + + legalName() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (legalName.asKnown() == null) 0 else 1) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BusinessInfo && + legalName == other.legalName && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(legalName, registrationNumber, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BusinessInfo{legalName=$legalName, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + customerType == other.customerType && + address == other.address && + beneficialOwners == other.beneficialOwners && + businessInfo == other.businessInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + customerType, + address, + beneficialOwners, + businessInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, customerType=$customerType, address=$address, beneficialOwners=$beneficialOwners, businessInfo=$businessInfo, additionalProperties=$additionalProperties}" + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerCreateParams && + createCustomerRequest == other.createCustomerRequest && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(createCustomerRequest, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CustomerCreateParams{createCustomerRequest=$createCustomerRequest, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteParams.kt new file mode 100644 index 00000000..2f2087a7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteParams.kt @@ -0,0 +1,222 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import java.util.Objects + +/** Delete a customer by their system-generated ID */ +class CustomerDeleteParams +private constructor( + private val customerId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun customerId(): String? = customerId + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): CustomerDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CustomerDeleteParams]. */ + fun builder() = Builder() + } + + /** A builder for [CustomerDeleteParams]. */ + class Builder internal constructor() { + + private var customerId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + internal fun from(customerDeleteParams: CustomerDeleteParams) = apply { + customerId = customerDeleteParams.customerId + additionalHeaders = customerDeleteParams.additionalHeaders.toBuilder() + additionalQueryParams = customerDeleteParams.additionalQueryParams.toBuilder() + additionalBodyProperties = customerDeleteParams.additionalBodyProperties.toMutableMap() + } + + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [CustomerDeleteParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CustomerDeleteParams = + CustomerDeleteParams( + customerId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Map? = additionalBodyProperties.ifEmpty { null } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> customerId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerDeleteParams && + customerId == other.customerId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash(customerId, additionalHeaders, additionalQueryParams, additionalBodyProperties) + + override fun toString() = + "CustomerDeleteParams{customerId=$customerId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteResponse.kt new file mode 100644 index 00000000..8fe51d4b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerDeleteResponse.kt @@ -0,0 +1,3753 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +@JsonDeserialize(using = CustomerDeleteResponse.Deserializer::class) +@JsonSerialize(using = CustomerDeleteResponse.Serializer::class) +class CustomerDeleteResponse +private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, +) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): CustomerDeleteResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerDeleteResponse && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "CustomerDeleteResponse{individual=$individual}" + business != null -> "CustomerDeleteResponse{business=$business}" + _json != null -> "CustomerDeleteResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid CustomerDeleteResponse") + } + + companion object { + + fun ofIndividual(individual: Individual) = CustomerDeleteResponse(individual = individual) + + fun ofBusiness(business: Business) = CustomerDeleteResponse(business = business) + } + + /** + * An interface that defines how to map each variant of [CustomerDeleteResponse] to a value of + * type [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [CustomerDeleteResponse] to a value of type [T]. + * + * An instance of [CustomerDeleteResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown CustomerDeleteResponse: $json") + } + } + + internal class Deserializer : + BaseDeserializer(CustomerDeleteResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): CustomerDeleteResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerDeleteResponse(individual = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerDeleteResponse(business = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> CustomerDeleteResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(CustomerDeleteResponse::class) { + + override fun serialize( + value: CustomerDeleteResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid CustomerDeleteResponse") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fullName(): String? = fullName.getNullable("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var fullName: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + platformCustomerId = individual.platformCustomerId + umaAddress = individual.umaAddress + id = individual.id + createdAt = individual.createdAt + isDeleted = individual.isDeleted + kycStatus = individual.kycStatus + updatedAt = individual.updatedAt + customerType = individual.customerType + address = individual.address + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun birthDate(birthDate: JsonField) = apply { this.birthDate = birthDate } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + birthDate, + fullName, + nationality, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + birthDate() + fullName() + nationality() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val beneficialOwners: JsonField>, + private val businessInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("beneficialOwners") + @ExcludeMissing + beneficialOwners: JsonField> = JsonMissing.of(), + @JsonProperty("businessInfo") + @ExcludeMissing + businessInfo: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun beneficialOwners(): List? = + beneficialOwners.getNullable("beneficialOwners") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun businessInfo(): BusinessInfo? = businessInfo.getNullable("businessInfo") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [beneficialOwners]. + * + * Unlike [beneficialOwners], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("beneficialOwners") + @ExcludeMissing + fun _beneficialOwners(): JsonField> = beneficialOwners + + /** + * Returns the raw JSON value of [businessInfo]. + * + * Unlike [businessInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("businessInfo") + @ExcludeMissing + fun _businessInfo(): JsonField = businessInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var beneficialOwners: JsonField>? = null + private var businessInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + platformCustomerId = business.platformCustomerId + umaAddress = business.umaAddress + id = business.id + createdAt = business.createdAt + isDeleted = business.isDeleted + kycStatus = business.kycStatus + updatedAt = business.updatedAt + customerType = business.customerType + address = business.address + beneficialOwners = business.beneficialOwners.map { it.toMutableList() } + businessInfo = business.businessInfo + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun beneficialOwners(beneficialOwners: List) = + beneficialOwners(JsonField.of(beneficialOwners)) + + /** + * Sets [Builder.beneficialOwners] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficialOwners] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun beneficialOwners(beneficialOwners: JsonField>) = apply { + this.beneficialOwners = beneficialOwners.map { it.toMutableList() } + } + + /** + * Adds a single [BeneficialOwner] to [beneficialOwners]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBeneficialOwner(beneficialOwner: BeneficialOwner) = apply { + beneficialOwners = + (beneficialOwners ?: JsonField.of(mutableListOf())).also { + checkKnown("beneficialOwners", it).add(beneficialOwner) + } + } + + fun businessInfo(businessInfo: BusinessInfo) = businessInfo(JsonField.of(businessInfo)) + + /** + * Sets [Builder.businessInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.businessInfo] with a well-typed [BusinessInfo] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun businessInfo(businessInfo: JsonField) = apply { + this.businessInfo = businessInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + (beneficialOwners ?: JsonMissing.of()).map { it.toImmutable() }, + businessInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + beneficialOwners()?.forEach { it.validate() } + businessInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (beneficialOwners.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (businessInfo.asKnown()?.validity() ?: 0) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + BUSINESS + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + class BeneficialOwner + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fullName: JsonField, + private val individualType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val emailAddress: JsonField, + private val nationality: JsonField, + private val percentageOwnership: JsonField, + private val phoneNumber: JsonField, + private val taxId: JsonField, + private val title: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("individualType") + @ExcludeMissing + individualType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("emailAddress") + @ExcludeMissing + emailAddress: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("percentageOwnership") + @ExcludeMissing + percentageOwnership: JsonField = JsonMissing.of(), + @JsonProperty("phoneNumber") + @ExcludeMissing + phoneNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + @JsonProperty("title") @ExcludeMissing title: JsonField = JsonMissing.of(), + ) : this( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + mutableMapOf(), + ) + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Type of individual in the corporation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun individualType(): IndividualType = individualType.getRequired("individualType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Email address of the individual + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun emailAddress(): String? = emailAddress.getNullable("emailAddress") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Percent of ownership when individual type is beneficial owner + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun percentageOwnership(): Double? = + percentageOwnership.getNullable("percentageOwnership") + + /** + * Phone number of the individual in E.164 format + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun phoneNumber(): String? = phoneNumber.getNullable("phoneNumber") + + /** + * Tax identification number of the individual. This could be a Social Security Number + * (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or + * a Passport Number. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Title at company + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun title(): String? = title.getNullable("title") + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [individualType]. + * + * Unlike [individualType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("individualType") + @ExcludeMissing + fun _individualType(): JsonField = individualType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [emailAddress]. + * + * Unlike [emailAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("emailAddress") + @ExcludeMissing + fun _emailAddress(): JsonField = emailAddress + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [percentageOwnership]. + * + * Unlike [percentageOwnership], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentageOwnership") + @ExcludeMissing + fun _percentageOwnership(): JsonField = percentageOwnership + + /** + * Returns the raw JSON value of [phoneNumber]. + * + * Unlike [phoneNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("phoneNumber") + @ExcludeMissing + fun _phoneNumber(): JsonField = phoneNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BeneficialOwner]. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BeneficialOwner]. */ + class Builder internal constructor() { + + private var fullName: JsonField? = null + private var individualType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var emailAddress: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var percentageOwnership: JsonField = JsonMissing.of() + private var phoneNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(beneficialOwner: BeneficialOwner) = apply { + fullName = beneficialOwner.fullName + individualType = beneficialOwner.individualType + address = beneficialOwner.address + birthDate = beneficialOwner.birthDate + emailAddress = beneficialOwner.emailAddress + nationality = beneficialOwner.nationality + percentageOwnership = beneficialOwner.percentageOwnership + phoneNumber = beneficialOwner.phoneNumber + taxId = beneficialOwner.taxId + title = beneficialOwner.title + additionalProperties = beneficialOwner.additionalProperties.toMutableMap() + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Type of individual in the corporation */ + fun individualType(individualType: IndividualType) = + individualType(JsonField.of(individualType)) + + /** + * Sets [Builder.individualType] to an arbitrary JSON value. + * + * You should usually call [Builder.individualType] with a well-typed + * [IndividualType] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun individualType(individualType: JsonField) = apply { + this.individualType = individualType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Email address of the individual */ + fun emailAddress(emailAddress: String) = emailAddress(JsonField.of(emailAddress)) + + /** + * Sets [Builder.emailAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.emailAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun emailAddress(emailAddress: JsonField) = apply { + this.emailAddress = emailAddress + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + /** Percent of ownership when individual type is beneficial owner */ + fun percentageOwnership(percentageOwnership: Double) = + percentageOwnership(JsonField.of(percentageOwnership)) + + /** + * Sets [Builder.percentageOwnership] to an arbitrary JSON value. + * + * You should usually call [Builder.percentageOwnership] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun percentageOwnership(percentageOwnership: JsonField) = apply { + this.percentageOwnership = percentageOwnership + } + + /** Phone number of the individual in E.164 format */ + fun phoneNumber(phoneNumber: String) = phoneNumber(JsonField.of(phoneNumber)) + + /** + * Sets [Builder.phoneNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.phoneNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun phoneNumber(phoneNumber: JsonField) = apply { + this.phoneNumber = phoneNumber + } + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + /** Title at company */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BeneficialOwner]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BeneficialOwner = + BeneficialOwner( + checkRequired("fullName", fullName), + checkRequired("individualType", individualType), + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BeneficialOwner = apply { + if (validated) { + return@apply + } + + fullName() + individualType().validate() + address()?.validate() + birthDate() + emailAddress() + nationality() + percentageOwnership() + phoneNumber() + taxId() + title() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (fullName.asKnown() == null) 0 else 1) + + (individualType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (emailAddress.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (if (percentageOwnership.asKnown() == null) 0 else 1) + + (if (phoneNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + (if (title.asKnown() == null) 0 else 1) + + /** Type of individual in the corporation */ + class IndividualType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val DIRECTOR = of("DIRECTOR") + + val CONTROL_PERSON = of("CONTROL_PERSON") + + val BUSINESS_POINT_OF_CONTACT = of("BUSINESS_POINT_OF_CONTACT") + + val TRUSTEE = of("TRUSTEE") + + val SETTLOR = of("SETTLOR") + + val GENERAL_PARTNER = of("GENERAL_PARTNER") + + fun of(value: String) = IndividualType(JsonField.of(value)) + } + + /** An enum containing [IndividualType]'s known values. */ + enum class Known { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + } + + /** + * An enum containing [IndividualType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [IndividualType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + /** + * An enum member indicating that [IndividualType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIRECTOR -> Value.DIRECTOR + CONTROL_PERSON -> Value.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Value.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Value.TRUSTEE + SETTLOR -> Value.SETTLOR + GENERAL_PARTNER -> Value.GENERAL_PARTNER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + DIRECTOR -> Known.DIRECTOR + CONTROL_PERSON -> Known.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Known.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Known.TRUSTEE + SETTLOR -> Known.SETTLOR + GENERAL_PARTNER -> Known.GENERAL_PARTNER + else -> throw GridInvalidDataException("Unknown IndividualType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): IndividualType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IndividualType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficialOwner && + fullName == other.fullName && + individualType == other.individualType && + address == other.address && + birthDate == other.birthDate && + emailAddress == other.emailAddress && + nationality == other.nationality && + percentageOwnership == other.percentageOwnership && + phoneNumber == other.phoneNumber && + taxId == other.taxId && + title == other.title && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BeneficialOwner{fullName=$fullName, individualType=$individualType, address=$address, birthDate=$birthDate, emailAddress=$emailAddress, nationality=$nationality, percentageOwnership=$percentageOwnership, phoneNumber=$phoneNumber, taxId=$taxId, title=$title, additionalProperties=$additionalProperties}" + } + + class BusinessInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val legalName: JsonField, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(legalName, registrationNumber, taxId, mutableMapOf()) + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun legalName(): String = legalName.getRequired("legalName") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun registrationNumber(): String? = registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("legalName") + @ExcludeMissing + fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BusinessInfo]. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BusinessInfo]. */ + class Builder internal constructor() { + + private var legalName: JsonField? = null + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(businessInfo: BusinessInfo) = apply { + legalName = businessInfo.legalName + registrationNumber = businessInfo.registrationNumber + taxId = businessInfo.taxId + additionalProperties = businessInfo.additionalProperties.toMutableMap() + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun legalName(legalName: JsonField) = apply { this.legalName = legalName } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BusinessInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BusinessInfo = + BusinessInfo( + checkRequired("legalName", legalName), + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BusinessInfo = apply { + if (validated) { + return@apply + } + + legalName() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (legalName.asKnown() == null) 0 else 1) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BusinessInfo && + legalName == other.legalName && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(legalName, registrationNumber, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BusinessInfo{legalName=$legalName, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + beneficialOwners == other.beneficialOwners && + businessInfo == other.businessInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, beneficialOwners=$beneficialOwners, businessInfo=$businessInfo, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParams.kt new file mode 100644 index 00000000..aed58715 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParams.kt @@ -0,0 +1,217 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** Generate a hosted KYC link to onboard a customer */ +class CustomerGetKycLinkParams +private constructor( + private val platformCustomerId: String, + private val redirectUri: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** The platform id of the customer to onboard */ + fun platformCustomerId(): String = platformCustomerId + + /** An optional uri a customer will be redirected to after completing the hosted KYC flow */ + fun redirectUri(): String? = redirectUri + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerGetKycLinkParams]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerGetKycLinkParams]. */ + class Builder internal constructor() { + + private var platformCustomerId: String? = null + private var redirectUri: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(customerGetKycLinkParams: CustomerGetKycLinkParams) = apply { + platformCustomerId = customerGetKycLinkParams.platformCustomerId + redirectUri = customerGetKycLinkParams.redirectUri + additionalHeaders = customerGetKycLinkParams.additionalHeaders.toBuilder() + additionalQueryParams = customerGetKycLinkParams.additionalQueryParams.toBuilder() + } + + /** The platform id of the customer to onboard */ + fun platformCustomerId(platformCustomerId: String) = apply { + this.platformCustomerId = platformCustomerId + } + + /** An optional uri a customer will be redirected to after completing the hosted KYC flow */ + fun redirectUri(redirectUri: String?) = apply { this.redirectUri = redirectUri } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [CustomerGetKycLinkParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerGetKycLinkParams = + CustomerGetKycLinkParams( + checkRequired("platformCustomerId", platformCustomerId), + redirectUri, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + put("platformCustomerId", platformCustomerId) + redirectUri?.let { put("redirectUri", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerGetKycLinkParams && + platformCustomerId == other.platformCustomerId && + redirectUri == other.redirectUri && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(platformCustomerId, redirectUri, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CustomerGetKycLinkParams{platformCustomerId=$platformCustomerId, redirectUri=$redirectUri, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponse.kt new file mode 100644 index 00000000..3e6e517e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponse.kt @@ -0,0 +1,242 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class CustomerGetKycLinkResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val customerId: JsonField, + private val kycUrl: JsonField, + private val platformCustomerId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("kycUrl") @ExcludeMissing kycUrl: JsonField = JsonMissing.of(), + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + ) : this(customerId, kycUrl, platformCustomerId, mutableMapOf()) + + /** + * The customer id of the newly created customer on the system + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * A hosted KYC link for your customer to complete KYC + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun kycUrl(): String? = kycUrl.getNullable("kycUrl") + + /** + * The platform id of the customer to onboard + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun platformCustomerId(): String? = platformCustomerId.getNullable("platformCustomerId") + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [kycUrl]. + * + * Unlike [kycUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycUrl") @ExcludeMissing fun _kycUrl(): JsonField = kycUrl + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerGetKycLinkResponse]. + */ + fun builder() = Builder() + } + + /** A builder for [CustomerGetKycLinkResponse]. */ + class Builder internal constructor() { + + private var customerId: JsonField = JsonMissing.of() + private var kycUrl: JsonField = JsonMissing.of() + private var platformCustomerId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(customerGetKycLinkResponse: CustomerGetKycLinkResponse) = apply { + customerId = customerGetKycLinkResponse.customerId + kycUrl = customerGetKycLinkResponse.kycUrl + platformCustomerId = customerGetKycLinkResponse.platformCustomerId + additionalProperties = customerGetKycLinkResponse.additionalProperties.toMutableMap() + } + + /** The customer id of the newly created customer on the system */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** A hosted KYC link for your customer to complete KYC */ + fun kycUrl(kycUrl: String) = kycUrl(JsonField.of(kycUrl)) + + /** + * Sets [Builder.kycUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.kycUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun kycUrl(kycUrl: JsonField) = apply { this.kycUrl = kycUrl } + + /** The platform id of the customer to onboard */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomerGetKycLinkResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CustomerGetKycLinkResponse = + CustomerGetKycLinkResponse( + customerId, + kycUrl, + platformCustomerId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CustomerGetKycLinkResponse = apply { + if (validated) { + return@apply + } + + customerId() + kycUrl() + platformCustomerId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (customerId.asKnown() == null) 0 else 1) + + (if (kycUrl.asKnown() == null) 0 else 1) + + (if (platformCustomerId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerGetKycLinkResponse && + customerId == other.customerId && + kycUrl == other.kycUrl && + platformCustomerId == other.platformCustomerId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(customerId, kycUrl, platformCustomerId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomerGetKycLinkResponse{customerId=$customerId, kycUrl=$kycUrl, platformCustomerId=$platformCustomerId, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPage.kt new file mode 100644 index 00000000..f4a9094e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPage.kt @@ -0,0 +1,148 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import com.grid.api.services.blocking.CustomerService +import java.util.Objects + +/** @see CustomerService.listInternalAccounts */ +class CustomerListInternalAccountsPage +private constructor( + private val service: CustomerService, + private val params: CustomerListInternalAccountsParams, + private val response: CustomerListInternalAccountsPageResponse, +) : Page { + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): CustomerListInternalAccountsParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): CustomerListInternalAccountsPage = + service.listInternalAccounts(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): CustomerListInternalAccountsParams = params + + /** The response that this page was parsed from. */ + fun response(): CustomerListInternalAccountsPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [CustomerListInternalAccountsPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListInternalAccountsPage]. */ + class Builder internal constructor() { + + private var service: CustomerService? = null + private var params: CustomerListInternalAccountsParams? = null + private var response: CustomerListInternalAccountsPageResponse? = null + + internal fun from(customerListInternalAccountsPage: CustomerListInternalAccountsPage) = + apply { + service = customerListInternalAccountsPage.service + params = customerListInternalAccountsPage.params + response = customerListInternalAccountsPage.response + } + + fun service(service: CustomerService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: CustomerListInternalAccountsParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: CustomerListInternalAccountsPageResponse) = apply { + this.response = response + } + + /** + * Returns an immutable instance of [CustomerListInternalAccountsPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerListInternalAccountsPage = + CustomerListInternalAccountsPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListInternalAccountsPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "CustomerListInternalAccountsPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageAsync.kt new file mode 100644 index 00000000..61e233b5 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageAsync.kt @@ -0,0 +1,149 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import com.grid.api.services.async.CustomerServiceAsync +import java.util.Objects + +/** @see CustomerServiceAsync.listInternalAccounts */ +class CustomerListInternalAccountsPageAsync +private constructor( + private val service: CustomerServiceAsync, + private val params: CustomerListInternalAccountsParams, + private val response: CustomerListInternalAccountsPageResponse, +) : PageAsync { + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [CustomerListInternalAccountsPageResponse], but gracefully handles missing data. + * + * @see CustomerListInternalAccountsPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): CustomerListInternalAccountsParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): CustomerListInternalAccountsPageAsync = + service.listInternalAccounts(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): CustomerListInternalAccountsParams = params + + /** The response that this page was parsed from. */ + fun response(): CustomerListInternalAccountsPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [CustomerListInternalAccountsPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListInternalAccountsPageAsync]. */ + class Builder internal constructor() { + + private var service: CustomerServiceAsync? = null + private var params: CustomerListInternalAccountsParams? = null + private var response: CustomerListInternalAccountsPageResponse? = null + + internal fun from( + customerListInternalAccountsPageAsync: CustomerListInternalAccountsPageAsync + ) = apply { + service = customerListInternalAccountsPageAsync.service + params = customerListInternalAccountsPageAsync.params + response = customerListInternalAccountsPageAsync.response + } + + fun service(service: CustomerServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: CustomerListInternalAccountsParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: CustomerListInternalAccountsPageResponse) = apply { + this.response = response + } + + /** + * Returns an immutable instance of [CustomerListInternalAccountsPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerListInternalAccountsPageAsync = + CustomerListInternalAccountsPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListInternalAccountsPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "CustomerListInternalAccountsPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponse.kt new file mode 100644 index 00000000..68bbc07b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponse.kt @@ -0,0 +1,306 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import java.util.Collections +import java.util.Objects + +class CustomerListInternalAccountsPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") + @ExcludeMissing + data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of internal accounts matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun hasMore(): Boolean = hasMore.getRequired("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of customers matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [CustomerListInternalAccountsPageResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListInternalAccountsPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField? = null + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from( + customerListInternalAccountsPageResponse: CustomerListInternalAccountsPageResponse + ) = apply { + data = customerListInternalAccountsPageResponse.data.map { it.toMutableList() } + hasMore = customerListInternalAccountsPageResponse.hasMore + nextCursor = customerListInternalAccountsPageResponse.nextCursor + totalCount = customerListInternalAccountsPageResponse.totalCount + additionalProperties = + customerListInternalAccountsPageResponse.additionalProperties.toMutableMap() + } + + /** List of internal accounts matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [InternalAccount] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: InternalAccount) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of customers matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomerListInternalAccountsPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerListInternalAccountsPageResponse = + CustomerListInternalAccountsPageResponse( + checkRequired("data", data).map { it.toImmutable() }, + checkRequired("hasMore", hasMore), + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CustomerListInternalAccountsPageResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListInternalAccountsPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomerListInternalAccountsPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParams.kt new file mode 100644 index 00000000..a8461175 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParams.kt @@ -0,0 +1,245 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Retrieve a list of internal accounts with optional filtering parameters. Returns all internal + * accounts that match the specified filters. If no filters are provided, returns all internal + * accounts (paginated). + * + * Internal accounts are created automatically when a customer is created based on the platform + * configuration. + */ +class CustomerListInternalAccountsParams +private constructor( + private val currency: String?, + private val cursor: String?, + private val customerId: String?, + private val limit: Long?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter by currency code */ + fun currency(): String? = currency + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** Filter by internal accounts associated with a specific customer */ + fun customerId(): String? = customerId + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): CustomerListInternalAccountsParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of + * [CustomerListInternalAccountsParams]. + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListInternalAccountsParams]. */ + class Builder internal constructor() { + + private var currency: String? = null + private var cursor: String? = null + private var customerId: String? = null + private var limit: Long? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(customerListInternalAccountsParams: CustomerListInternalAccountsParams) = + apply { + currency = customerListInternalAccountsParams.currency + cursor = customerListInternalAccountsParams.cursor + customerId = customerListInternalAccountsParams.customerId + limit = customerListInternalAccountsParams.limit + additionalHeaders = customerListInternalAccountsParams.additionalHeaders.toBuilder() + additionalQueryParams = + customerListInternalAccountsParams.additionalQueryParams.toBuilder() + } + + /** Filter by currency code */ + fun currency(currency: String?) = apply { this.currency = currency } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** Filter by internal accounts associated with a specific customer */ + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [CustomerListInternalAccountsParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CustomerListInternalAccountsParams = + CustomerListInternalAccountsParams( + currency, + cursor, + customerId, + limit, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + currency?.let { put("currency", it) } + cursor?.let { put("cursor", it) } + customerId?.let { put("customerId", it) } + limit?.let { put("limit", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListInternalAccountsParams && + currency == other.currency && + cursor == other.cursor && + customerId == other.customerId && + limit == other.limit && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(currency, cursor, customerId, limit, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CustomerListInternalAccountsParams{currency=$currency, cursor=$cursor, customerId=$customerId, limit=$limit, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPage.kt new file mode 100644 index 00000000..51049d7f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPage.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.services.blocking.CustomerService +import java.util.Objects + +/** @see CustomerService.list */ +class CustomerListPage +private constructor( + private val service: CustomerService, + private val params: CustomerListParams, + private val response: CustomerListPageResponse, +) : Page { + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): CustomerListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): CustomerListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): CustomerListParams = params + + /** The response that this page was parsed from. */ + fun response(): CustomerListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerListPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListPage]. */ + class Builder internal constructor() { + + private var service: CustomerService? = null + private var params: CustomerListParams? = null + private var response: CustomerListPageResponse? = null + + internal fun from(customerListPage: CustomerListPage) = apply { + service = customerListPage.service + params = customerListPage.params + response = customerListPage.response + } + + fun service(service: CustomerService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: CustomerListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: CustomerListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [CustomerListPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerListPage = + CustomerListPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "CustomerListPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageAsync.kt new file mode 100644 index 00000000..09e5aacc --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageAsync.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.services.async.CustomerServiceAsync +import java.util.Objects + +/** @see CustomerServiceAsync.list */ +class CustomerListPageAsync +private constructor( + private val service: CustomerServiceAsync, + private val params: CustomerListParams, + private val response: CustomerListPageResponse, +) : PageAsync { + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [CustomerListPageResponse], but gracefully handles missing data. + * + * @see CustomerListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): CustomerListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): CustomerListPageAsync = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): CustomerListParams = params + + /** The response that this page was parsed from. */ + fun response(): CustomerListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerListPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListPageAsync]. */ + class Builder internal constructor() { + + private var service: CustomerServiceAsync? = null + private var params: CustomerListParams? = null + private var response: CustomerListPageResponse? = null + + internal fun from(customerListPageAsync: CustomerListPageAsync) = apply { + service = customerListPageAsync.service + params = customerListPageAsync.params + response = customerListPageAsync.response + } + + fun service(service: CustomerServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: CustomerListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: CustomerListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [CustomerListPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerListPageAsync = + CustomerListPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "CustomerListPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageResponse.kt new file mode 100644 index 00000000..967273af --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListPageResponse.kt @@ -0,0 +1,308 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class CustomerListPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") + @ExcludeMissing + data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of customers matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun hasMore(): Boolean = hasMore.getRequired("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of customers matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerListPageResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerListPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField? = null + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(customerListPageResponse: CustomerListPageResponse) = apply { + data = customerListPageResponse.data.map { it.toMutableList() } + hasMore = customerListPageResponse.hasMore + nextCursor = customerListPageResponse.nextCursor + totalCount = customerListPageResponse.totalCount + additionalProperties = customerListPageResponse.additionalProperties.toMutableMap() + } + + /** List of customers matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [CustomerOneOf] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: CustomerOneOf) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Alias for calling [addData] with `CustomerOneOf.ofIndividual(individual)`. */ + fun addData(individual: CustomerOneOf.Individual) = + addData(CustomerOneOf.ofIndividual(individual)) + + /** Alias for calling [addData] with `CustomerOneOf.ofBusiness(business)`. */ + fun addData(business: CustomerOneOf.Business) = addData(CustomerOneOf.ofBusiness(business)) + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of customers matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomerListPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerListPageResponse = + CustomerListPageResponse( + checkRequired("data", data).map { it.toImmutable() }, + checkRequired("hasMore", hasMore), + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CustomerListPageResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomerListPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListParams.kt new file mode 100644 index 00000000..04999340 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerListParams.kt @@ -0,0 +1,479 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.util.Objects + +/** + * Retrieve a list of customers with optional filtering parameters. Returns all customers that match + * the specified filters. If no filters are provided, returns all customers (paginated). + */ +class CustomerListParams +private constructor( + private val createdAfter: OffsetDateTime?, + private val createdBefore: OffsetDateTime?, + private val cursor: String?, + private val customerType: CustomerType?, + private val isIncludingDeleted: Boolean?, + private val limit: Long?, + private val platformCustomerId: String?, + private val umaAddress: String?, + private val updatedAfter: OffsetDateTime?, + private val updatedBefore: OffsetDateTime?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter customers created after this timestamp (inclusive) */ + fun createdAfter(): OffsetDateTime? = createdAfter + + /** Filter customers created before this timestamp (inclusive) */ + fun createdBefore(): OffsetDateTime? = createdBefore + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** Filter by customer type */ + fun customerType(): CustomerType? = customerType + + /** Whether to include deleted customers in the results. Default is false. */ + fun isIncludingDeleted(): Boolean? = isIncludingDeleted + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Filter by platform-specific customer identifier */ + fun platformCustomerId(): String? = platformCustomerId + + /** Filter by uma address */ + fun umaAddress(): String? = umaAddress + + /** Filter customers updated after this timestamp (inclusive) */ + fun updatedAfter(): OffsetDateTime? = updatedAfter + + /** Filter customers updated before this timestamp (inclusive) */ + fun updatedBefore(): OffsetDateTime? = updatedBefore + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): CustomerListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CustomerListParams]. */ + fun builder() = Builder() + } + + /** A builder for [CustomerListParams]. */ + class Builder internal constructor() { + + private var createdAfter: OffsetDateTime? = null + private var createdBefore: OffsetDateTime? = null + private var cursor: String? = null + private var customerType: CustomerType? = null + private var isIncludingDeleted: Boolean? = null + private var limit: Long? = null + private var platformCustomerId: String? = null + private var umaAddress: String? = null + private var updatedAfter: OffsetDateTime? = null + private var updatedBefore: OffsetDateTime? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(customerListParams: CustomerListParams) = apply { + createdAfter = customerListParams.createdAfter + createdBefore = customerListParams.createdBefore + cursor = customerListParams.cursor + customerType = customerListParams.customerType + isIncludingDeleted = customerListParams.isIncludingDeleted + limit = customerListParams.limit + platformCustomerId = customerListParams.platformCustomerId + umaAddress = customerListParams.umaAddress + updatedAfter = customerListParams.updatedAfter + updatedBefore = customerListParams.updatedBefore + additionalHeaders = customerListParams.additionalHeaders.toBuilder() + additionalQueryParams = customerListParams.additionalQueryParams.toBuilder() + } + + /** Filter customers created after this timestamp (inclusive) */ + fun createdAfter(createdAfter: OffsetDateTime?) = apply { this.createdAfter = createdAfter } + + /** Filter customers created before this timestamp (inclusive) */ + fun createdBefore(createdBefore: OffsetDateTime?) = apply { + this.createdBefore = createdBefore + } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** Filter by customer type */ + fun customerType(customerType: CustomerType?) = apply { this.customerType = customerType } + + /** Whether to include deleted customers in the results. Default is false. */ + fun isIncludingDeleted(isIncludingDeleted: Boolean?) = apply { + this.isIncludingDeleted = isIncludingDeleted + } + + /** + * Alias for [Builder.isIncludingDeleted]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun isIncludingDeleted(isIncludingDeleted: Boolean) = + isIncludingDeleted(isIncludingDeleted as Boolean?) + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Filter by platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String?) = apply { + this.platformCustomerId = platformCustomerId + } + + /** Filter by uma address */ + fun umaAddress(umaAddress: String?) = apply { this.umaAddress = umaAddress } + + /** Filter customers updated after this timestamp (inclusive) */ + fun updatedAfter(updatedAfter: OffsetDateTime?) = apply { this.updatedAfter = updatedAfter } + + /** Filter customers updated before this timestamp (inclusive) */ + fun updatedBefore(updatedBefore: OffsetDateTime?) = apply { + this.updatedBefore = updatedBefore + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [CustomerListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CustomerListParams = + CustomerListParams( + createdAfter, + createdBefore, + cursor, + customerType, + isIncludingDeleted, + limit, + platformCustomerId, + umaAddress, + updatedAfter, + updatedBefore, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + createdAfter?.let { + put("createdAfter", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + createdBefore?.let { + put("createdBefore", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + cursor?.let { put("cursor", it) } + customerType?.let { put("customerType", it.toString()) } + isIncludingDeleted?.let { put("isIncludingDeleted", it.toString()) } + limit?.let { put("limit", it.toString()) } + platformCustomerId?.let { put("platformCustomerId", it) } + umaAddress?.let { put("umaAddress", it) } + updatedAfter?.let { + put("updatedAfter", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + updatedBefore?.let { + put("updatedBefore", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + putAll(additionalQueryParams) + } + .build() + + /** Filter by customer type */ + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL, + BUSINESS, + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerListParams && + createdAfter == other.createdAfter && + createdBefore == other.createdBefore && + cursor == other.cursor && + customerType == other.customerType && + isIncludingDeleted == other.isIncludingDeleted && + limit == other.limit && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + updatedAfter == other.updatedAfter && + updatedBefore == other.updatedBefore && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + createdAfter, + createdBefore, + cursor, + customerType, + isIncludingDeleted, + limit, + platformCustomerId, + umaAddress, + updatedAfter, + updatedBefore, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "CustomerListParams{createdAfter=$createdAfter, createdBefore=$createdBefore, cursor=$cursor, customerType=$customerType, isIncludingDeleted=$isIncludingDeleted, limit=$limit, platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerOneOf.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerOneOf.kt new file mode 100644 index 00000000..e05aaddc --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerOneOf.kt @@ -0,0 +1,3749 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +@JsonDeserialize(using = CustomerOneOf.Deserializer::class) +@JsonSerialize(using = CustomerOneOf.Serializer::class) +class CustomerOneOf +private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, +) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): CustomerOneOf = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerOneOf && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "CustomerOneOf{individual=$individual}" + business != null -> "CustomerOneOf{business=$business}" + _json != null -> "CustomerOneOf{_unknown=$_json}" + else -> throw IllegalStateException("Invalid CustomerOneOf") + } + + companion object { + + fun ofIndividual(individual: Individual) = CustomerOneOf(individual = individual) + + fun ofBusiness(business: Business) = CustomerOneOf(business = business) + } + + /** + * An interface that defines how to map each variant of [CustomerOneOf] to a value of type [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [CustomerOneOf] to a value of type [T]. + * + * An instance of [CustomerOneOf] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older version + * than the API, then the API may respond with new variants that the SDK is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown CustomerOneOf: $json") + } + } + + internal class Deserializer : BaseDeserializer(CustomerOneOf::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): CustomerOneOf { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerOneOf(individual = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerOneOf(business = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> CustomerOneOf(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(CustomerOneOf::class) { + + override fun serialize( + value: CustomerOneOf, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid CustomerOneOf") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fullName(): String? = fullName.getNullable("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var fullName: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + platformCustomerId = individual.platformCustomerId + umaAddress = individual.umaAddress + id = individual.id + createdAt = individual.createdAt + isDeleted = individual.isDeleted + kycStatus = individual.kycStatus + updatedAt = individual.updatedAt + customerType = individual.customerType + address = individual.address + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun birthDate(birthDate: JsonField) = apply { this.birthDate = birthDate } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + birthDate, + fullName, + nationality, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + birthDate() + fullName() + nationality() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val beneficialOwners: JsonField>, + private val businessInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("beneficialOwners") + @ExcludeMissing + beneficialOwners: JsonField> = JsonMissing.of(), + @JsonProperty("businessInfo") + @ExcludeMissing + businessInfo: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun beneficialOwners(): List? = + beneficialOwners.getNullable("beneficialOwners") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun businessInfo(): BusinessInfo? = businessInfo.getNullable("businessInfo") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [beneficialOwners]. + * + * Unlike [beneficialOwners], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("beneficialOwners") + @ExcludeMissing + fun _beneficialOwners(): JsonField> = beneficialOwners + + /** + * Returns the raw JSON value of [businessInfo]. + * + * Unlike [businessInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("businessInfo") + @ExcludeMissing + fun _businessInfo(): JsonField = businessInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var beneficialOwners: JsonField>? = null + private var businessInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + platformCustomerId = business.platformCustomerId + umaAddress = business.umaAddress + id = business.id + createdAt = business.createdAt + isDeleted = business.isDeleted + kycStatus = business.kycStatus + updatedAt = business.updatedAt + customerType = business.customerType + address = business.address + beneficialOwners = business.beneficialOwners.map { it.toMutableList() } + businessInfo = business.businessInfo + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun beneficialOwners(beneficialOwners: List) = + beneficialOwners(JsonField.of(beneficialOwners)) + + /** + * Sets [Builder.beneficialOwners] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficialOwners] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun beneficialOwners(beneficialOwners: JsonField>) = apply { + this.beneficialOwners = beneficialOwners.map { it.toMutableList() } + } + + /** + * Adds a single [BeneficialOwner] to [beneficialOwners]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBeneficialOwner(beneficialOwner: BeneficialOwner) = apply { + beneficialOwners = + (beneficialOwners ?: JsonField.of(mutableListOf())).also { + checkKnown("beneficialOwners", it).add(beneficialOwner) + } + } + + fun businessInfo(businessInfo: BusinessInfo) = businessInfo(JsonField.of(businessInfo)) + + /** + * Sets [Builder.businessInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.businessInfo] with a well-typed [BusinessInfo] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun businessInfo(businessInfo: JsonField) = apply { + this.businessInfo = businessInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + (beneficialOwners ?: JsonMissing.of()).map { it.toImmutable() }, + businessInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + beneficialOwners()?.forEach { it.validate() } + businessInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (beneficialOwners.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (businessInfo.asKnown()?.validity() ?: 0) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + BUSINESS + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + class BeneficialOwner + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fullName: JsonField, + private val individualType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val emailAddress: JsonField, + private val nationality: JsonField, + private val percentageOwnership: JsonField, + private val phoneNumber: JsonField, + private val taxId: JsonField, + private val title: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("individualType") + @ExcludeMissing + individualType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("emailAddress") + @ExcludeMissing + emailAddress: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("percentageOwnership") + @ExcludeMissing + percentageOwnership: JsonField = JsonMissing.of(), + @JsonProperty("phoneNumber") + @ExcludeMissing + phoneNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + @JsonProperty("title") @ExcludeMissing title: JsonField = JsonMissing.of(), + ) : this( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + mutableMapOf(), + ) + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Type of individual in the corporation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun individualType(): IndividualType = individualType.getRequired("individualType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Email address of the individual + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun emailAddress(): String? = emailAddress.getNullable("emailAddress") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Percent of ownership when individual type is beneficial owner + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun percentageOwnership(): Double? = + percentageOwnership.getNullable("percentageOwnership") + + /** + * Phone number of the individual in E.164 format + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun phoneNumber(): String? = phoneNumber.getNullable("phoneNumber") + + /** + * Tax identification number of the individual. This could be a Social Security Number + * (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or + * a Passport Number. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Title at company + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun title(): String? = title.getNullable("title") + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [individualType]. + * + * Unlike [individualType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("individualType") + @ExcludeMissing + fun _individualType(): JsonField = individualType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [emailAddress]. + * + * Unlike [emailAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("emailAddress") + @ExcludeMissing + fun _emailAddress(): JsonField = emailAddress + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [percentageOwnership]. + * + * Unlike [percentageOwnership], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentageOwnership") + @ExcludeMissing + fun _percentageOwnership(): JsonField = percentageOwnership + + /** + * Returns the raw JSON value of [phoneNumber]. + * + * Unlike [phoneNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("phoneNumber") + @ExcludeMissing + fun _phoneNumber(): JsonField = phoneNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BeneficialOwner]. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BeneficialOwner]. */ + class Builder internal constructor() { + + private var fullName: JsonField? = null + private var individualType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var emailAddress: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var percentageOwnership: JsonField = JsonMissing.of() + private var phoneNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(beneficialOwner: BeneficialOwner) = apply { + fullName = beneficialOwner.fullName + individualType = beneficialOwner.individualType + address = beneficialOwner.address + birthDate = beneficialOwner.birthDate + emailAddress = beneficialOwner.emailAddress + nationality = beneficialOwner.nationality + percentageOwnership = beneficialOwner.percentageOwnership + phoneNumber = beneficialOwner.phoneNumber + taxId = beneficialOwner.taxId + title = beneficialOwner.title + additionalProperties = beneficialOwner.additionalProperties.toMutableMap() + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Type of individual in the corporation */ + fun individualType(individualType: IndividualType) = + individualType(JsonField.of(individualType)) + + /** + * Sets [Builder.individualType] to an arbitrary JSON value. + * + * You should usually call [Builder.individualType] with a well-typed + * [IndividualType] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun individualType(individualType: JsonField) = apply { + this.individualType = individualType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Email address of the individual */ + fun emailAddress(emailAddress: String) = emailAddress(JsonField.of(emailAddress)) + + /** + * Sets [Builder.emailAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.emailAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun emailAddress(emailAddress: JsonField) = apply { + this.emailAddress = emailAddress + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + /** Percent of ownership when individual type is beneficial owner */ + fun percentageOwnership(percentageOwnership: Double) = + percentageOwnership(JsonField.of(percentageOwnership)) + + /** + * Sets [Builder.percentageOwnership] to an arbitrary JSON value. + * + * You should usually call [Builder.percentageOwnership] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun percentageOwnership(percentageOwnership: JsonField) = apply { + this.percentageOwnership = percentageOwnership + } + + /** Phone number of the individual in E.164 format */ + fun phoneNumber(phoneNumber: String) = phoneNumber(JsonField.of(phoneNumber)) + + /** + * Sets [Builder.phoneNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.phoneNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun phoneNumber(phoneNumber: JsonField) = apply { + this.phoneNumber = phoneNumber + } + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + /** Title at company */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BeneficialOwner]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BeneficialOwner = + BeneficialOwner( + checkRequired("fullName", fullName), + checkRequired("individualType", individualType), + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BeneficialOwner = apply { + if (validated) { + return@apply + } + + fullName() + individualType().validate() + address()?.validate() + birthDate() + emailAddress() + nationality() + percentageOwnership() + phoneNumber() + taxId() + title() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (fullName.asKnown() == null) 0 else 1) + + (individualType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (emailAddress.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (if (percentageOwnership.asKnown() == null) 0 else 1) + + (if (phoneNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + (if (title.asKnown() == null) 0 else 1) + + /** Type of individual in the corporation */ + class IndividualType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val DIRECTOR = of("DIRECTOR") + + val CONTROL_PERSON = of("CONTROL_PERSON") + + val BUSINESS_POINT_OF_CONTACT = of("BUSINESS_POINT_OF_CONTACT") + + val TRUSTEE = of("TRUSTEE") + + val SETTLOR = of("SETTLOR") + + val GENERAL_PARTNER = of("GENERAL_PARTNER") + + fun of(value: String) = IndividualType(JsonField.of(value)) + } + + /** An enum containing [IndividualType]'s known values. */ + enum class Known { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + } + + /** + * An enum containing [IndividualType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [IndividualType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + /** + * An enum member indicating that [IndividualType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIRECTOR -> Value.DIRECTOR + CONTROL_PERSON -> Value.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Value.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Value.TRUSTEE + SETTLOR -> Value.SETTLOR + GENERAL_PARTNER -> Value.GENERAL_PARTNER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + DIRECTOR -> Known.DIRECTOR + CONTROL_PERSON -> Known.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Known.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Known.TRUSTEE + SETTLOR -> Known.SETTLOR + GENERAL_PARTNER -> Known.GENERAL_PARTNER + else -> throw GridInvalidDataException("Unknown IndividualType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): IndividualType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IndividualType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficialOwner && + fullName == other.fullName && + individualType == other.individualType && + address == other.address && + birthDate == other.birthDate && + emailAddress == other.emailAddress && + nationality == other.nationality && + percentageOwnership == other.percentageOwnership && + phoneNumber == other.phoneNumber && + taxId == other.taxId && + title == other.title && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BeneficialOwner{fullName=$fullName, individualType=$individualType, address=$address, birthDate=$birthDate, emailAddress=$emailAddress, nationality=$nationality, percentageOwnership=$percentageOwnership, phoneNumber=$phoneNumber, taxId=$taxId, title=$title, additionalProperties=$additionalProperties}" + } + + class BusinessInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val legalName: JsonField, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(legalName, registrationNumber, taxId, mutableMapOf()) + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun legalName(): String = legalName.getRequired("legalName") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun registrationNumber(): String? = registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("legalName") + @ExcludeMissing + fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BusinessInfo]. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BusinessInfo]. */ + class Builder internal constructor() { + + private var legalName: JsonField? = null + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(businessInfo: BusinessInfo) = apply { + legalName = businessInfo.legalName + registrationNumber = businessInfo.registrationNumber + taxId = businessInfo.taxId + additionalProperties = businessInfo.additionalProperties.toMutableMap() + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun legalName(legalName: JsonField) = apply { this.legalName = legalName } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BusinessInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BusinessInfo = + BusinessInfo( + checkRequired("legalName", legalName), + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BusinessInfo = apply { + if (validated) { + return@apply + } + + legalName() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (legalName.asKnown() == null) 0 else 1) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BusinessInfo && + legalName == other.legalName && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(legalName, registrationNumber, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BusinessInfo{legalName=$legalName, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + beneficialOwners == other.beneficialOwners && + businessInfo == other.businessInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, beneficialOwners=$beneficialOwners, businessInfo=$businessInfo, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveParams.kt new file mode 100644 index 00000000..084da364 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveParams.kt @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** Retrieve a customer by their system-generated ID */ +class CustomerRetrieveParams +private constructor( + private val customerId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun customerId(): String? = customerId + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): CustomerRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CustomerRetrieveParams]. */ + fun builder() = Builder() + } + + /** A builder for [CustomerRetrieveParams]. */ + class Builder internal constructor() { + + private var customerId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(customerRetrieveParams: CustomerRetrieveParams) = apply { + customerId = customerRetrieveParams.customerId + additionalHeaders = customerRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = customerRetrieveParams.additionalQueryParams.toBuilder() + } + + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [CustomerRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CustomerRetrieveParams = + CustomerRetrieveParams( + customerId, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> customerId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerRetrieveParams && + customerId == other.customerId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(customerId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CustomerRetrieveParams{customerId=$customerId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveResponse.kt new file mode 100644 index 00000000..89480197 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerRetrieveResponse.kt @@ -0,0 +1,3753 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +@JsonDeserialize(using = CustomerRetrieveResponse.Deserializer::class) +@JsonSerialize(using = CustomerRetrieveResponse.Serializer::class) +class CustomerRetrieveResponse +private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, +) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): CustomerRetrieveResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerRetrieveResponse && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "CustomerRetrieveResponse{individual=$individual}" + business != null -> "CustomerRetrieveResponse{business=$business}" + _json != null -> "CustomerRetrieveResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid CustomerRetrieveResponse") + } + + companion object { + + fun ofIndividual(individual: Individual) = CustomerRetrieveResponse(individual = individual) + + fun ofBusiness(business: Business) = CustomerRetrieveResponse(business = business) + } + + /** + * An interface that defines how to map each variant of [CustomerRetrieveResponse] to a value of + * type [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [CustomerRetrieveResponse] to a value of type [T]. + * + * An instance of [CustomerRetrieveResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown CustomerRetrieveResponse: $json") + } + } + + internal class Deserializer : + BaseDeserializer(CustomerRetrieveResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): CustomerRetrieveResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerRetrieveResponse(individual = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerRetrieveResponse(business = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> CustomerRetrieveResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(CustomerRetrieveResponse::class) { + + override fun serialize( + value: CustomerRetrieveResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid CustomerRetrieveResponse") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fullName(): String? = fullName.getNullable("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var fullName: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + platformCustomerId = individual.platformCustomerId + umaAddress = individual.umaAddress + id = individual.id + createdAt = individual.createdAt + isDeleted = individual.isDeleted + kycStatus = individual.kycStatus + updatedAt = individual.updatedAt + customerType = individual.customerType + address = individual.address + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun birthDate(birthDate: JsonField) = apply { this.birthDate = birthDate } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + birthDate, + fullName, + nationality, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + birthDate() + fullName() + nationality() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val beneficialOwners: JsonField>, + private val businessInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("beneficialOwners") + @ExcludeMissing + beneficialOwners: JsonField> = JsonMissing.of(), + @JsonProperty("businessInfo") + @ExcludeMissing + businessInfo: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun beneficialOwners(): List? = + beneficialOwners.getNullable("beneficialOwners") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun businessInfo(): BusinessInfo? = businessInfo.getNullable("businessInfo") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [beneficialOwners]. + * + * Unlike [beneficialOwners], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("beneficialOwners") + @ExcludeMissing + fun _beneficialOwners(): JsonField> = beneficialOwners + + /** + * Returns the raw JSON value of [businessInfo]. + * + * Unlike [businessInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("businessInfo") + @ExcludeMissing + fun _businessInfo(): JsonField = businessInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var beneficialOwners: JsonField>? = null + private var businessInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + platformCustomerId = business.platformCustomerId + umaAddress = business.umaAddress + id = business.id + createdAt = business.createdAt + isDeleted = business.isDeleted + kycStatus = business.kycStatus + updatedAt = business.updatedAt + customerType = business.customerType + address = business.address + beneficialOwners = business.beneficialOwners.map { it.toMutableList() } + businessInfo = business.businessInfo + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun beneficialOwners(beneficialOwners: List) = + beneficialOwners(JsonField.of(beneficialOwners)) + + /** + * Sets [Builder.beneficialOwners] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficialOwners] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun beneficialOwners(beneficialOwners: JsonField>) = apply { + this.beneficialOwners = beneficialOwners.map { it.toMutableList() } + } + + /** + * Adds a single [BeneficialOwner] to [beneficialOwners]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBeneficialOwner(beneficialOwner: BeneficialOwner) = apply { + beneficialOwners = + (beneficialOwners ?: JsonField.of(mutableListOf())).also { + checkKnown("beneficialOwners", it).add(beneficialOwner) + } + } + + fun businessInfo(businessInfo: BusinessInfo) = businessInfo(JsonField.of(businessInfo)) + + /** + * Sets [Builder.businessInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.businessInfo] with a well-typed [BusinessInfo] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun businessInfo(businessInfo: JsonField) = apply { + this.businessInfo = businessInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + (beneficialOwners ?: JsonMissing.of()).map { it.toImmutable() }, + businessInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + beneficialOwners()?.forEach { it.validate() } + businessInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (beneficialOwners.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (businessInfo.asKnown()?.validity() ?: 0) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + BUSINESS + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + class BeneficialOwner + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fullName: JsonField, + private val individualType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val emailAddress: JsonField, + private val nationality: JsonField, + private val percentageOwnership: JsonField, + private val phoneNumber: JsonField, + private val taxId: JsonField, + private val title: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("individualType") + @ExcludeMissing + individualType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("emailAddress") + @ExcludeMissing + emailAddress: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("percentageOwnership") + @ExcludeMissing + percentageOwnership: JsonField = JsonMissing.of(), + @JsonProperty("phoneNumber") + @ExcludeMissing + phoneNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + @JsonProperty("title") @ExcludeMissing title: JsonField = JsonMissing.of(), + ) : this( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + mutableMapOf(), + ) + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Type of individual in the corporation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun individualType(): IndividualType = individualType.getRequired("individualType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Email address of the individual + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun emailAddress(): String? = emailAddress.getNullable("emailAddress") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Percent of ownership when individual type is beneficial owner + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun percentageOwnership(): Double? = + percentageOwnership.getNullable("percentageOwnership") + + /** + * Phone number of the individual in E.164 format + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun phoneNumber(): String? = phoneNumber.getNullable("phoneNumber") + + /** + * Tax identification number of the individual. This could be a Social Security Number + * (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or + * a Passport Number. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Title at company + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun title(): String? = title.getNullable("title") + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [individualType]. + * + * Unlike [individualType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("individualType") + @ExcludeMissing + fun _individualType(): JsonField = individualType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [emailAddress]. + * + * Unlike [emailAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("emailAddress") + @ExcludeMissing + fun _emailAddress(): JsonField = emailAddress + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [percentageOwnership]. + * + * Unlike [percentageOwnership], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentageOwnership") + @ExcludeMissing + fun _percentageOwnership(): JsonField = percentageOwnership + + /** + * Returns the raw JSON value of [phoneNumber]. + * + * Unlike [phoneNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("phoneNumber") + @ExcludeMissing + fun _phoneNumber(): JsonField = phoneNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BeneficialOwner]. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BeneficialOwner]. */ + class Builder internal constructor() { + + private var fullName: JsonField? = null + private var individualType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var emailAddress: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var percentageOwnership: JsonField = JsonMissing.of() + private var phoneNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(beneficialOwner: BeneficialOwner) = apply { + fullName = beneficialOwner.fullName + individualType = beneficialOwner.individualType + address = beneficialOwner.address + birthDate = beneficialOwner.birthDate + emailAddress = beneficialOwner.emailAddress + nationality = beneficialOwner.nationality + percentageOwnership = beneficialOwner.percentageOwnership + phoneNumber = beneficialOwner.phoneNumber + taxId = beneficialOwner.taxId + title = beneficialOwner.title + additionalProperties = beneficialOwner.additionalProperties.toMutableMap() + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Type of individual in the corporation */ + fun individualType(individualType: IndividualType) = + individualType(JsonField.of(individualType)) + + /** + * Sets [Builder.individualType] to an arbitrary JSON value. + * + * You should usually call [Builder.individualType] with a well-typed + * [IndividualType] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun individualType(individualType: JsonField) = apply { + this.individualType = individualType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Email address of the individual */ + fun emailAddress(emailAddress: String) = emailAddress(JsonField.of(emailAddress)) + + /** + * Sets [Builder.emailAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.emailAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun emailAddress(emailAddress: JsonField) = apply { + this.emailAddress = emailAddress + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + /** Percent of ownership when individual type is beneficial owner */ + fun percentageOwnership(percentageOwnership: Double) = + percentageOwnership(JsonField.of(percentageOwnership)) + + /** + * Sets [Builder.percentageOwnership] to an arbitrary JSON value. + * + * You should usually call [Builder.percentageOwnership] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun percentageOwnership(percentageOwnership: JsonField) = apply { + this.percentageOwnership = percentageOwnership + } + + /** Phone number of the individual in E.164 format */ + fun phoneNumber(phoneNumber: String) = phoneNumber(JsonField.of(phoneNumber)) + + /** + * Sets [Builder.phoneNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.phoneNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun phoneNumber(phoneNumber: JsonField) = apply { + this.phoneNumber = phoneNumber + } + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + /** Title at company */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BeneficialOwner]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BeneficialOwner = + BeneficialOwner( + checkRequired("fullName", fullName), + checkRequired("individualType", individualType), + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BeneficialOwner = apply { + if (validated) { + return@apply + } + + fullName() + individualType().validate() + address()?.validate() + birthDate() + emailAddress() + nationality() + percentageOwnership() + phoneNumber() + taxId() + title() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (fullName.asKnown() == null) 0 else 1) + + (individualType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (emailAddress.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (if (percentageOwnership.asKnown() == null) 0 else 1) + + (if (phoneNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + (if (title.asKnown() == null) 0 else 1) + + /** Type of individual in the corporation */ + class IndividualType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val DIRECTOR = of("DIRECTOR") + + val CONTROL_PERSON = of("CONTROL_PERSON") + + val BUSINESS_POINT_OF_CONTACT = of("BUSINESS_POINT_OF_CONTACT") + + val TRUSTEE = of("TRUSTEE") + + val SETTLOR = of("SETTLOR") + + val GENERAL_PARTNER = of("GENERAL_PARTNER") + + fun of(value: String) = IndividualType(JsonField.of(value)) + } + + /** An enum containing [IndividualType]'s known values. */ + enum class Known { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + } + + /** + * An enum containing [IndividualType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [IndividualType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + /** + * An enum member indicating that [IndividualType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIRECTOR -> Value.DIRECTOR + CONTROL_PERSON -> Value.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Value.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Value.TRUSTEE + SETTLOR -> Value.SETTLOR + GENERAL_PARTNER -> Value.GENERAL_PARTNER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + DIRECTOR -> Known.DIRECTOR + CONTROL_PERSON -> Known.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Known.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Known.TRUSTEE + SETTLOR -> Known.SETTLOR + GENERAL_PARTNER -> Known.GENERAL_PARTNER + else -> throw GridInvalidDataException("Unknown IndividualType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): IndividualType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IndividualType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficialOwner && + fullName == other.fullName && + individualType == other.individualType && + address == other.address && + birthDate == other.birthDate && + emailAddress == other.emailAddress && + nationality == other.nationality && + percentageOwnership == other.percentageOwnership && + phoneNumber == other.phoneNumber && + taxId == other.taxId && + title == other.title && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BeneficialOwner{fullName=$fullName, individualType=$individualType, address=$address, birthDate=$birthDate, emailAddress=$emailAddress, nationality=$nationality, percentageOwnership=$percentageOwnership, phoneNumber=$phoneNumber, taxId=$taxId, title=$title, additionalProperties=$additionalProperties}" + } + + class BusinessInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val legalName: JsonField, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(legalName, registrationNumber, taxId, mutableMapOf()) + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun legalName(): String = legalName.getRequired("legalName") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun registrationNumber(): String? = registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("legalName") + @ExcludeMissing + fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BusinessInfo]. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BusinessInfo]. */ + class Builder internal constructor() { + + private var legalName: JsonField? = null + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(businessInfo: BusinessInfo) = apply { + legalName = businessInfo.legalName + registrationNumber = businessInfo.registrationNumber + taxId = businessInfo.taxId + additionalProperties = businessInfo.additionalProperties.toMutableMap() + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun legalName(legalName: JsonField) = apply { this.legalName = legalName } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BusinessInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BusinessInfo = + BusinessInfo( + checkRequired("legalName", legalName), + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BusinessInfo = apply { + if (validated) { + return@apply + } + + legalName() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (legalName.asKnown() == null) 0 else 1) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BusinessInfo && + legalName == other.legalName && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(legalName, registrationNumber, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BusinessInfo{legalName=$legalName, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + beneficialOwners == other.beneficialOwners && + businessInfo == other.businessInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, beneficialOwners=$beneficialOwners, businessInfo=$businessInfo, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdate.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdate.kt new file mode 100644 index 00000000..c14ff5e7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdate.kt @@ -0,0 +1,159 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class CustomerUpdate +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val umaAddress: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("umaAddress") @ExcludeMissing umaAddress: JsonField = JsonMissing.of() + ) : this(umaAddress, mutableMapOf()) + + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be updated. + * This is an optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun umaAddress(): String? = umaAddress.getNullable("umaAddress") + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") @ExcludeMissing fun _umaAddress(): JsonField = umaAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [CustomerUpdate]. */ + fun builder() = Builder() + } + + /** A builder for [CustomerUpdate]. */ + class Builder internal constructor() { + + private var umaAddress: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(customerUpdate: CustomerUpdate) = apply { + umaAddress = customerUpdate.umaAddress + additionalProperties = customerUpdate.additionalProperties.toMutableMap() + } + + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be updated. + * This is an optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CustomerUpdate]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CustomerUpdate = + CustomerUpdate(umaAddress, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): CustomerUpdate = apply { + if (validated) { + return@apply + } + + umaAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (umaAddress.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerUpdate && + umaAddress == other.umaAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(umaAddress, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CustomerUpdate{umaAddress=$umaAddress, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateParams.kt new file mode 100644 index 00000000..1ced2c3e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateParams.kt @@ -0,0 +1,3537 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.util.Collections +import java.util.Objects + +/** Update a customer's metadata by their system-generated ID */ +class CustomerUpdateParams +private constructor( + private val customerId: String?, + private val updateCustomerRequest: UpdateCustomerRequest, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun customerId(): String? = customerId + + fun updateCustomerRequest(): UpdateCustomerRequest = updateCustomerRequest + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CustomerUpdateParams]. + * + * The following fields are required: + * ```kotlin + * .updateCustomerRequest() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CustomerUpdateParams]. */ + class Builder internal constructor() { + + private var customerId: String? = null + private var updateCustomerRequest: UpdateCustomerRequest? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(customerUpdateParams: CustomerUpdateParams) = apply { + customerId = customerUpdateParams.customerId + updateCustomerRequest = customerUpdateParams.updateCustomerRequest + additionalHeaders = customerUpdateParams.additionalHeaders.toBuilder() + additionalQueryParams = customerUpdateParams.additionalQueryParams.toBuilder() + } + + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + fun updateCustomerRequest(updateCustomerRequest: UpdateCustomerRequest) = apply { + this.updateCustomerRequest = updateCustomerRequest + } + + /** + * Alias for calling [updateCustomerRequest] with + * `UpdateCustomerRequest.ofIndividual(individual)`. + */ + fun updateCustomerRequest(individual: UpdateCustomerRequest.Individual) = + updateCustomerRequest(UpdateCustomerRequest.ofIndividual(individual)) + + /** + * Alias for calling [updateCustomerRequest] with + * `UpdateCustomerRequest.ofBusiness(business)`. + */ + fun updateCustomerRequest(business: UpdateCustomerRequest.Business) = + updateCustomerRequest(UpdateCustomerRequest.ofBusiness(business)) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [CustomerUpdateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .updateCustomerRequest() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CustomerUpdateParams = + CustomerUpdateParams( + customerId, + checkRequired("updateCustomerRequest", updateCustomerRequest), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): UpdateCustomerRequest = updateCustomerRequest + + fun _pathParam(index: Int): String = + when (index) { + 0 -> customerId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + @JsonDeserialize(using = UpdateCustomerRequest.Deserializer::class) + @JsonSerialize(using = UpdateCustomerRequest.Serializer::class) + class UpdateCustomerRequest + private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, + ) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): UpdateCustomerRequest = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UpdateCustomerRequest && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "UpdateCustomerRequest{individual=$individual}" + business != null -> "UpdateCustomerRequest{business=$business}" + _json != null -> "UpdateCustomerRequest{_unknown=$_json}" + else -> throw IllegalStateException("Invalid UpdateCustomerRequest") + } + + companion object { + + fun ofIndividual(individual: Individual) = + UpdateCustomerRequest(individual = individual) + + fun ofBusiness(business: Business) = UpdateCustomerRequest(business = business) + } + + /** + * An interface that defines how to map each variant of [UpdateCustomerRequest] to a value + * of type [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [UpdateCustomerRequest] to a value of type [T]. + * + * An instance of [UpdateCustomerRequest] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK + * is on an older version than the API, then the API may respond with new variants that + * the SDK is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown UpdateCustomerRequest: $json") + } + } + + internal class Deserializer : + BaseDeserializer(UpdateCustomerRequest::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): UpdateCustomerRequest { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + UpdateCustomerRequest(individual = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + UpdateCustomerRequest(business = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> UpdateCustomerRequest(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(UpdateCustomerRequest::class) { + + override fun serialize( + value: UpdateCustomerRequest, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid UpdateCustomerRequest") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val umaAddress: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + ) : this( + umaAddress, + customerType, + address, + birthDate, + fullName, + nationality, + mutableMapOf(), + ) + + fun toCustomerUpdate(): CustomerUpdate = + CustomerUpdate.builder().umaAddress(umaAddress).build() + + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be + * updated. This is an optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun umaAddress(): String? = umaAddress.getNullable("umaAddress") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun fullName(): String? = fullName.getNullable("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var umaAddress: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var fullName: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + umaAddress = individual.umaAddress + customerType = individual.customerType + address = individual.address + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be + * updated. This is an optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { + this.umaAddress = umaAddress + } + + fun customerType(customerType: CustomerType) = + customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + umaAddress, + checkRequired("customerType", customerType), + address, + birthDate, + fullName, + nationality, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + umaAddress() + customerType().validate() + address()?.validate() + birthDate() + fullName() + nationality() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (umaAddress.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + class CustomerType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + /** + * An enum member indicating that [CustomerType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + umaAddress == other.umaAddress && + customerType == other.customerType && + address == other.address && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + umaAddress, + customerType, + address, + birthDate, + fullName, + nationality, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{umaAddress=$umaAddress, customerType=$customerType, address=$address, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val umaAddress: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val beneficialOwners: JsonField>, + private val businessInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("beneficialOwners") + @ExcludeMissing + beneficialOwners: JsonField> = JsonMissing.of(), + @JsonProperty("businessInfo") + @ExcludeMissing + businessInfo: JsonField = JsonMissing.of(), + ) : this( + umaAddress, + customerType, + address, + beneficialOwners, + businessInfo, + mutableMapOf(), + ) + + fun toCustomerUpdate(): CustomerUpdate = + CustomerUpdate.builder().umaAddress(umaAddress).build() + + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be + * updated. This is an optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun umaAddress(): String? = umaAddress.getNullable("umaAddress") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun beneficialOwners(): List? = + beneficialOwners.getNullable("beneficialOwners") + + /** + * Additional information for business entities + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun businessInfo(): BusinessInfo? = businessInfo.getNullable("businessInfo") + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [beneficialOwners]. + * + * Unlike [beneficialOwners], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("beneficialOwners") + @ExcludeMissing + fun _beneficialOwners(): JsonField> = beneficialOwners + + /** + * Returns the raw JSON value of [businessInfo]. + * + * Unlike [businessInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("businessInfo") + @ExcludeMissing + fun _businessInfo(): JsonField = businessInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var umaAddress: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var beneficialOwners: JsonField>? = null + private var businessInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + umaAddress = business.umaAddress + customerType = business.customerType + address = business.address + beneficialOwners = business.beneficialOwners.map { it.toMutableList() } + businessInfo = business.businessInfo + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** + * Optional UMA address identifier. If provided, the customer's UMA address will be + * updated. This is an optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { + this.umaAddress = umaAddress + } + + fun customerType(customerType: CustomerType) = + customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun beneficialOwners(beneficialOwners: List) = + beneficialOwners(JsonField.of(beneficialOwners)) + + /** + * Sets [Builder.beneficialOwners] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficialOwners] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun beneficialOwners(beneficialOwners: JsonField>) = apply { + this.beneficialOwners = beneficialOwners.map { it.toMutableList() } + } + + /** + * Adds a single [BeneficialOwner] to [beneficialOwners]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBeneficialOwner(beneficialOwner: BeneficialOwner) = apply { + beneficialOwners = + (beneficialOwners ?: JsonField.of(mutableListOf())).also { + checkKnown("beneficialOwners", it).add(beneficialOwner) + } + } + + /** Additional information for business entities */ + fun businessInfo(businessInfo: BusinessInfo) = + businessInfo(JsonField.of(businessInfo)) + + /** + * Sets [Builder.businessInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.businessInfo] with a well-typed [BusinessInfo] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun businessInfo(businessInfo: JsonField) = apply { + this.businessInfo = businessInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + umaAddress, + checkRequired("customerType", customerType), + address, + (beneficialOwners ?: JsonMissing.of()).map { it.toImmutable() }, + businessInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + umaAddress() + customerType().validate() + address()?.validate() + beneficialOwners()?.forEach { it.validate() } + businessInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (umaAddress.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (beneficialOwners.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (businessInfo.asKnown()?.validity() ?: 0) + + class CustomerType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + BUSINESS + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + class BeneficialOwner + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fullName: JsonField, + private val individualType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val emailAddress: JsonField, + private val nationality: JsonField, + private val percentageOwnership: JsonField, + private val phoneNumber: JsonField, + private val taxId: JsonField, + private val title: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("individualType") + @ExcludeMissing + individualType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("emailAddress") + @ExcludeMissing + emailAddress: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("percentageOwnership") + @ExcludeMissing + percentageOwnership: JsonField = JsonMissing.of(), + @JsonProperty("phoneNumber") + @ExcludeMissing + phoneNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") + @ExcludeMissing + taxId: JsonField = JsonMissing.of(), + @JsonProperty("title") + @ExcludeMissing + title: JsonField = JsonMissing.of(), + ) : this( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + mutableMapOf(), + ) + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Type of individual in the corporation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun individualType(): IndividualType = individualType.getRequired("individualType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Email address of the individual + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun emailAddress(): String? = emailAddress.getNullable("emailAddress") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Percent of ownership when individual type is beneficial owner + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun percentageOwnership(): Double? = + percentageOwnership.getNullable("percentageOwnership") + + /** + * Phone number of the individual in E.164 format + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun phoneNumber(): String? = phoneNumber.getNullable("phoneNumber") + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Title at company + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun title(): String? = title.getNullable("title") + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") + @ExcludeMissing + fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [individualType]. + * + * Unlike [individualType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("individualType") + @ExcludeMissing + fun _individualType(): JsonField = individualType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("address") + @ExcludeMissing + fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [emailAddress]. + * + * Unlike [emailAddress], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("emailAddress") + @ExcludeMissing + fun _emailAddress(): JsonField = emailAddress + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [percentageOwnership]. + * + * Unlike [percentageOwnership], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentageOwnership") + @ExcludeMissing + fun _percentageOwnership(): JsonField = percentageOwnership + + /** + * Returns the raw JSON value of [phoneNumber]. + * + * Unlike [phoneNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("phoneNumber") + @ExcludeMissing + fun _phoneNumber(): JsonField = phoneNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BeneficialOwner]. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BeneficialOwner]. */ + class Builder internal constructor() { + + private var fullName: JsonField? = null + private var individualType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var emailAddress: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var percentageOwnership: JsonField = JsonMissing.of() + private var phoneNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(beneficialOwner: BeneficialOwner) = apply { + fullName = beneficialOwner.fullName + individualType = beneficialOwner.individualType + address = beneficialOwner.address + birthDate = beneficialOwner.birthDate + emailAddress = beneficialOwner.emailAddress + nationality = beneficialOwner.nationality + percentageOwnership = beneficialOwner.percentageOwnership + phoneNumber = beneficialOwner.phoneNumber + taxId = beneficialOwner.taxId + title = beneficialOwner.title + additionalProperties = beneficialOwner.additionalProperties.toMutableMap() + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Type of individual in the corporation */ + fun individualType(individualType: IndividualType) = + individualType(JsonField.of(individualType)) + + /** + * Sets [Builder.individualType] to an arbitrary JSON value. + * + * You should usually call [Builder.individualType] with a well-typed + * [IndividualType] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun individualType(individualType: JsonField) = apply { + this.individualType = individualType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Email address of the individual */ + fun emailAddress(emailAddress: String) = + emailAddress(JsonField.of(emailAddress)) + + /** + * Sets [Builder.emailAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.emailAddress] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun emailAddress(emailAddress: JsonField) = apply { + this.emailAddress = emailAddress + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + /** Percent of ownership when individual type is beneficial owner */ + fun percentageOwnership(percentageOwnership: Double) = + percentageOwnership(JsonField.of(percentageOwnership)) + + /** + * Sets [Builder.percentageOwnership] to an arbitrary JSON value. + * + * You should usually call [Builder.percentageOwnership] with a well-typed + * [Double] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun percentageOwnership(percentageOwnership: JsonField) = apply { + this.percentageOwnership = percentageOwnership + } + + /** Phone number of the individual in E.164 format */ + fun phoneNumber(phoneNumber: String) = phoneNumber(JsonField.of(phoneNumber)) + + /** + * Sets [Builder.phoneNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.phoneNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun phoneNumber(phoneNumber: JsonField) = apply { + this.phoneNumber = phoneNumber + } + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + /** Title at company */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BeneficialOwner]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BeneficialOwner = + BeneficialOwner( + checkRequired("fullName", fullName), + checkRequired("individualType", individualType), + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BeneficialOwner = apply { + if (validated) { + return@apply + } + + fullName() + individualType().validate() + address()?.validate() + birthDate() + emailAddress() + nationality() + percentageOwnership() + phoneNumber() + taxId() + title() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (fullName.asKnown() == null) 0 else 1) + + (individualType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (emailAddress.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (if (percentageOwnership.asKnown() == null) 0 else 1) + + (if (phoneNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + (if (title.asKnown() == null) 0 else 1) + + /** Type of individual in the corporation */ + class IndividualType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue + fun _value(): JsonField = value + + companion object { + + val DIRECTOR = of("DIRECTOR") + + val CONTROL_PERSON = of("CONTROL_PERSON") + + val BUSINESS_POINT_OF_CONTACT = of("BUSINESS_POINT_OF_CONTACT") + + val TRUSTEE = of("TRUSTEE") + + val SETTLOR = of("SETTLOR") + + val GENERAL_PARTNER = of("GENERAL_PARTNER") + + fun of(value: String) = IndividualType(JsonField.of(value)) + } + + /** An enum containing [IndividualType]'s known values. */ + enum class Known { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + } + + /** + * An enum containing [IndividualType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [IndividualType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For + * example, if the SDK is on an older version than the API, then the API may + * respond with new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + /** + * An enum member indicating that [IndividualType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or + * if you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIRECTOR -> Value.DIRECTOR + CONTROL_PERSON -> Value.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Value.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Value.TRUSTEE + SETTLOR -> Value.SETTLOR + GENERAL_PARTNER -> Value.GENERAL_PARTNER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known + * and don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + DIRECTOR -> Known.DIRECTOR + CONTROL_PERSON -> Known.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Known.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Known.TRUSTEE + SETTLOR -> Known.SETTLOR + GENERAL_PARTNER -> Known.GENERAL_PARTNER + else -> throw GridInvalidDataException("Unknown IndividualType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have + * the expected primitive type. + */ + fun asString(): String = + _value().asString() + ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): IndividualType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IndividualType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or + * is unexpectedly missing or null (e.g. if the server responded with an + * unexpected value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("country") + @ExcludeMissing + fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] + * value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties( + additionalProperties: Map + ) = apply { this.additionalProperties.putAll(additionalProperties) } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficialOwner && + fullName == other.fullName && + individualType == other.individualType && + address == other.address && + birthDate == other.birthDate && + emailAddress == other.emailAddress && + nationality == other.nationality && + percentageOwnership == other.percentageOwnership && + phoneNumber == other.phoneNumber && + taxId == other.taxId && + title == other.title && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BeneficialOwner{fullName=$fullName, individualType=$individualType, address=$address, birthDate=$birthDate, emailAddress=$emailAddress, nationality=$nationality, percentageOwnership=$percentageOwnership, phoneNumber=$phoneNumber, taxId=$taxId, title=$title, additionalProperties=$additionalProperties}" + } + + /** Additional information for business entities */ + class BusinessInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val legalName: JsonField, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") + @ExcludeMissing + taxId: JsonField = JsonMissing.of(), + ) : this(legalName, registrationNumber, taxId, mutableMapOf()) + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun legalName(): String? = legalName.getNullable("legalName") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun registrationNumber(): String? = + registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("legalName") + @ExcludeMissing + fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [BusinessInfo]. */ + fun builder() = Builder() + } + + /** A builder for [BusinessInfo]. */ + class Builder internal constructor() { + + private var legalName: JsonField = JsonMissing.of() + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(businessInfo: BusinessInfo) = apply { + legalName = businessInfo.legalName + registrationNumber = businessInfo.registrationNumber + taxId = businessInfo.taxId + additionalProperties = businessInfo.additionalProperties.toMutableMap() + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun legalName(legalName: JsonField) = apply { + this.legalName = legalName + } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed + * [String] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BusinessInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BusinessInfo = + BusinessInfo( + legalName, + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BusinessInfo = apply { + if (validated) { + return@apply + } + + legalName() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (legalName.asKnown() == null) 0 else 1) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BusinessInfo && + legalName == other.legalName && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(legalName, registrationNumber, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BusinessInfo{legalName=$legalName, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + umaAddress == other.umaAddress && + customerType == other.customerType && + address == other.address && + beneficialOwners == other.beneficialOwners && + businessInfo == other.businessInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + umaAddress, + customerType, + address, + beneficialOwners, + businessInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{umaAddress=$umaAddress, customerType=$customerType, address=$address, beneficialOwners=$beneficialOwners, businessInfo=$businessInfo, additionalProperties=$additionalProperties}" + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerUpdateParams && + customerId == other.customerId && + updateCustomerRequest == other.updateCustomerRequest && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(customerId, updateCustomerRequest, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CustomerUpdateParams{customerId=$customerId, updateCustomerRequest=$updateCustomerRequest, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateResponse.kt new file mode 100644 index 00000000..9534b2fb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/CustomerUpdateResponse.kt @@ -0,0 +1,3753 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +@JsonDeserialize(using = CustomerUpdateResponse.Deserializer::class) +@JsonSerialize(using = CustomerUpdateResponse.Serializer::class) +class CustomerUpdateResponse +private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, +) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): CustomerUpdateResponse = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerUpdateResponse && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "CustomerUpdateResponse{individual=$individual}" + business != null -> "CustomerUpdateResponse{business=$business}" + _json != null -> "CustomerUpdateResponse{_unknown=$_json}" + else -> throw IllegalStateException("Invalid CustomerUpdateResponse") + } + + companion object { + + fun ofIndividual(individual: Individual) = CustomerUpdateResponse(individual = individual) + + fun ofBusiness(business: Business) = CustomerUpdateResponse(business = business) + } + + /** + * An interface that defines how to map each variant of [CustomerUpdateResponse] to a value of + * type [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [CustomerUpdateResponse] to a value of type [T]. + * + * An instance of [CustomerUpdateResponse] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown CustomerUpdateResponse: $json") + } + } + + internal class Deserializer : + BaseDeserializer(CustomerUpdateResponse::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): CustomerUpdateResponse { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerUpdateResponse(individual = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + CustomerUpdateResponse(business = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> CustomerUpdateResponse(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(CustomerUpdateResponse::class) { + + override fun serialize( + value: CustomerUpdateResponse, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid CustomerUpdateResponse") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fullName(): String? = fullName.getNullable("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var fullName: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + platformCustomerId = individual.platformCustomerId + umaAddress = individual.umaAddress + id = individual.id + createdAt = individual.createdAt + isDeleted = individual.isDeleted + kycStatus = individual.kycStatus + updatedAt = individual.updatedAt + customerType = individual.customerType + address = individual.address + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun birthDate(birthDate: JsonField) = apply { this.birthDate = birthDate } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + birthDate, + fullName, + nationality, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + birthDate() + fullName() + nationality() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INDIVIDUAL = of("INDIVIDUAL") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + INDIVIDUAL + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INDIVIDUAL, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + INDIVIDUAL -> Value.INDIVIDUAL + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + INDIVIDUAL -> Known.INDIVIDUAL + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + birthDate, + fullName, + nationality, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val platformCustomerId: JsonField, + private val umaAddress: JsonField, + private val id: JsonField, + private val createdAt: JsonField, + private val isDeleted: JsonField, + private val kycStatus: JsonField, + private val updatedAt: JsonField, + private val customerType: JsonField, + private val address: JsonField
, + private val beneficialOwners: JsonField>, + private val businessInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("isDeleted") + @ExcludeMissing + isDeleted: JsonField = JsonMissing.of(), + @JsonProperty("kycStatus") + @ExcludeMissing + kycStatus: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerType") + @ExcludeMissing + customerType: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("beneficialOwners") + @ExcludeMissing + beneficialOwners: JsonField> = JsonMissing.of(), + @JsonProperty("businessInfo") + @ExcludeMissing + businessInfo: JsonField = JsonMissing.of(), + ) : this( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + mutableMapOf(), + ) + + fun toCustomer(): Customer = + Customer.builder() + .platformCustomerId(platformCustomerId) + .umaAddress(umaAddress) + .id(id) + .createdAt(createdAt) + .isDeleted(isDeleted) + .kycStatus(kycStatus) + .updatedAt(updatedAt) + .build() + + /** + * Platform-specific customer identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): String? = id.getNullable("id") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Whether the customer is marked as deleted + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isDeleted(): Boolean? = isDeleted.getNullable("isDeleted") + + /** + * The current KYC status of a customer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun kycStatus(): Customer.KycStatus? = kycStatus.getNullable("kycStatus") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerType(): CustomerType = customerType.getRequired("customerType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun beneficialOwners(): List? = + beneficialOwners.getNullable("beneficialOwners") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun businessInfo(): BusinessInfo? = businessInfo.getNullable("businessInfo") + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [isDeleted]. + * + * Unlike [isDeleted], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("isDeleted") @ExcludeMissing fun _isDeleted(): JsonField = isDeleted + + /** + * Returns the raw JSON value of [kycStatus]. + * + * Unlike [kycStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kycStatus") + @ExcludeMissing + fun _kycStatus(): JsonField = kycStatus + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerType]. + * + * Unlike [customerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("customerType") + @ExcludeMissing + fun _customerType(): JsonField = customerType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [beneficialOwners]. + * + * Unlike [beneficialOwners], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("beneficialOwners") + @ExcludeMissing + fun _beneficialOwners(): JsonField> = beneficialOwners + + /** + * Returns the raw JSON value of [businessInfo]. + * + * Unlike [businessInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("businessInfo") + @ExcludeMissing + fun _businessInfo(): JsonField = businessInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var platformCustomerId: JsonField? = null + private var umaAddress: JsonField? = null + private var id: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var isDeleted: JsonField = JsonMissing.of() + private var kycStatus: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var customerType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var beneficialOwners: JsonField>? = null + private var businessInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + platformCustomerId = business.platformCustomerId + umaAddress = business.umaAddress + id = business.id + createdAt = business.createdAt + isDeleted = business.isDeleted + kycStatus = business.kycStatus + updatedAt = business.updatedAt + customerType = business.customerType + address = business.address + beneficialOwners = business.beneficialOwners.map { it.toMutableList() } + businessInfo = business.businessInfo + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** Platform-specific customer identifier */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** + * Full UMA address (always present in responses, even if system-generated). This is an + * optional identifier to route payments to the customer. + */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** Whether the customer is marked as deleted */ + fun isDeleted(isDeleted: Boolean) = isDeleted(JsonField.of(isDeleted)) + + /** + * Sets [Builder.isDeleted] to an arbitrary JSON value. + * + * You should usually call [Builder.isDeleted] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isDeleted(isDeleted: JsonField) = apply { this.isDeleted = isDeleted } + + /** The current KYC status of a customer */ + fun kycStatus(kycStatus: Customer.KycStatus) = kycStatus(JsonField.of(kycStatus)) + + /** + * Sets [Builder.kycStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.kycStatus] with a well-typed [Customer.KycStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun kycStatus(kycStatus: JsonField) = apply { + this.kycStatus = kycStatus + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + fun customerType(customerType: CustomerType) = customerType(JsonField.of(customerType)) + + /** + * Sets [Builder.customerType] to an arbitrary JSON value. + * + * You should usually call [Builder.customerType] with a well-typed [CustomerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerType(customerType: JsonField) = apply { + this.customerType = customerType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun beneficialOwners(beneficialOwners: List) = + beneficialOwners(JsonField.of(beneficialOwners)) + + /** + * Sets [Builder.beneficialOwners] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficialOwners] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun beneficialOwners(beneficialOwners: JsonField>) = apply { + this.beneficialOwners = beneficialOwners.map { it.toMutableList() } + } + + /** + * Adds a single [BeneficialOwner] to [beneficialOwners]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addBeneficialOwner(beneficialOwner: BeneficialOwner) = apply { + beneficialOwners = + (beneficialOwners ?: JsonField.of(mutableListOf())).also { + checkKnown("beneficialOwners", it).add(beneficialOwner) + } + } + + fun businessInfo(businessInfo: BusinessInfo) = businessInfo(JsonField.of(businessInfo)) + + /** + * Sets [Builder.businessInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.businessInfo] with a well-typed [BusinessInfo] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun businessInfo(businessInfo: JsonField) = apply { + this.businessInfo = businessInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .platformCustomerId() + * .umaAddress() + * .customerType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("umaAddress", umaAddress), + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + checkRequired("customerType", customerType), + address, + (beneficialOwners ?: JsonMissing.of()).map { it.toImmutable() }, + businessInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + platformCustomerId() + umaAddress() + id() + createdAt() + isDeleted() + kycStatus()?.validate() + updatedAt() + customerType().validate() + address()?.validate() + beneficialOwners()?.forEach { it.validate() } + businessInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (if (id.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (isDeleted.asKnown() == null) 0 else 1) + + (kycStatus.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (customerType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (beneficialOwners.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (businessInfo.asKnown()?.validity() ?: 0) + + class CustomerType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BUSINESS = of("BUSINESS") + + fun of(value: String) = CustomerType(JsonField.of(value)) + } + + /** An enum containing [CustomerType]'s known values. */ + enum class Known { + BUSINESS + } + + /** + * An enum containing [CustomerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CustomerType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BUSINESS, + /** + * An enum member indicating that [CustomerType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BUSINESS -> Value.BUSINESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BUSINESS -> Known.BUSINESS + else -> throw GridInvalidDataException("Unknown CustomerType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): CustomerType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CustomerType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + class BeneficialOwner + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fullName: JsonField, + private val individualType: JsonField, + private val address: JsonField
, + private val birthDate: JsonField, + private val emailAddress: JsonField, + private val nationality: JsonField, + private val percentageOwnership: JsonField, + private val phoneNumber: JsonField, + private val taxId: JsonField, + private val title: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("individualType") + @ExcludeMissing + individualType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField
= JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("emailAddress") + @ExcludeMissing + emailAddress: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("percentageOwnership") + @ExcludeMissing + percentageOwnership: JsonField = JsonMissing.of(), + @JsonProperty("phoneNumber") + @ExcludeMissing + phoneNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + @JsonProperty("title") @ExcludeMissing title: JsonField = JsonMissing.of(), + ) : this( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + mutableMapOf(), + ) + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Type of individual in the corporation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun individualType(): IndividualType = individualType.getRequired("individualType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun birthDate(): LocalDate? = birthDate.getNullable("birthDate") + + /** + * Email address of the individual + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun emailAddress(): String? = emailAddress.getNullable("emailAddress") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun nationality(): String? = nationality.getNullable("nationality") + + /** + * Percent of ownership when individual type is beneficial owner + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun percentageOwnership(): Double? = + percentageOwnership.getNullable("percentageOwnership") + + /** + * Phone number of the individual in E.164 format + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun phoneNumber(): String? = phoneNumber.getNullable("phoneNumber") + + /** + * Tax identification number of the individual. This could be a Social Security Number + * (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or + * a Passport Number. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Title at company + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun title(): String? = title.getNullable("title") + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [individualType]. + * + * Unlike [individualType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("individualType") + @ExcludeMissing + fun _individualType(): JsonField = individualType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [emailAddress]. + * + * Unlike [emailAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("emailAddress") + @ExcludeMissing + fun _emailAddress(): JsonField = emailAddress + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [percentageOwnership]. + * + * Unlike [percentageOwnership], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentageOwnership") + @ExcludeMissing + fun _percentageOwnership(): JsonField = percentageOwnership + + /** + * Returns the raw JSON value of [phoneNumber]. + * + * Unlike [phoneNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("phoneNumber") + @ExcludeMissing + fun _phoneNumber(): JsonField = phoneNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BeneficialOwner]. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BeneficialOwner]. */ + class Builder internal constructor() { + + private var fullName: JsonField? = null + private var individualType: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var birthDate: JsonField = JsonMissing.of() + private var emailAddress: JsonField = JsonMissing.of() + private var nationality: JsonField = JsonMissing.of() + private var percentageOwnership: JsonField = JsonMissing.of() + private var phoneNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(beneficialOwner: BeneficialOwner) = apply { + fullName = beneficialOwner.fullName + individualType = beneficialOwner.individualType + address = beneficialOwner.address + birthDate = beneficialOwner.birthDate + emailAddress = beneficialOwner.emailAddress + nationality = beneficialOwner.nationality + percentageOwnership = beneficialOwner.percentageOwnership + phoneNumber = beneficialOwner.phoneNumber + taxId = beneficialOwner.taxId + title = beneficialOwner.title + additionalProperties = beneficialOwner.additionalProperties.toMutableMap() + } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Type of individual in the corporation */ + fun individualType(individualType: IndividualType) = + individualType(JsonField.of(individualType)) + + /** + * Sets [Builder.individualType] to an arbitrary JSON value. + * + * You should usually call [Builder.individualType] with a well-typed + * [IndividualType] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun individualType(individualType: JsonField) = apply { + this.individualType = individualType + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun birthDate(birthDate: JsonField) = apply { + this.birthDate = birthDate + } + + /** Email address of the individual */ + fun emailAddress(emailAddress: String) = emailAddress(JsonField.of(emailAddress)) + + /** + * Sets [Builder.emailAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.emailAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun emailAddress(emailAddress: JsonField) = apply { + this.emailAddress = emailAddress + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + /** Percent of ownership when individual type is beneficial owner */ + fun percentageOwnership(percentageOwnership: Double) = + percentageOwnership(JsonField.of(percentageOwnership)) + + /** + * Sets [Builder.percentageOwnership] to an arbitrary JSON value. + * + * You should usually call [Builder.percentageOwnership] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun percentageOwnership(percentageOwnership: JsonField) = apply { + this.percentageOwnership = percentageOwnership + } + + /** Phone number of the individual in E.164 format */ + fun phoneNumber(phoneNumber: String) = phoneNumber(JsonField.of(phoneNumber)) + + /** + * Sets [Builder.phoneNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.phoneNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun phoneNumber(phoneNumber: JsonField) = apply { + this.phoneNumber = phoneNumber + } + + /** + * Tax identification number of the individual. This could be a Social Security + * Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US + * individuals, or a Passport Number. + */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + /** Title at company */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BeneficialOwner]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .fullName() + * .individualType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BeneficialOwner = + BeneficialOwner( + checkRequired("fullName", fullName), + checkRequired("individualType", individualType), + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BeneficialOwner = apply { + if (validated) { + return@apply + } + + fullName() + individualType().validate() + address()?.validate() + birthDate() + emailAddress() + nationality() + percentageOwnership() + phoneNumber() + taxId() + title() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (fullName.asKnown() == null) 0 else 1) + + (individualType.asKnown()?.validity() ?: 0) + + (address.asKnown()?.validity() ?: 0) + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (emailAddress.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (if (percentageOwnership.asKnown() == null) 0 else 1) + + (if (phoneNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + (if (title.asKnown() == null) 0 else 1) + + /** Type of individual in the corporation */ + class IndividualType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val DIRECTOR = of("DIRECTOR") + + val CONTROL_PERSON = of("CONTROL_PERSON") + + val BUSINESS_POINT_OF_CONTACT = of("BUSINESS_POINT_OF_CONTACT") + + val TRUSTEE = of("TRUSTEE") + + val SETTLOR = of("SETTLOR") + + val GENERAL_PARTNER = of("GENERAL_PARTNER") + + fun of(value: String) = IndividualType(JsonField.of(value)) + } + + /** An enum containing [IndividualType]'s known values. */ + enum class Known { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + } + + /** + * An enum containing [IndividualType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [IndividualType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIRECTOR, + CONTROL_PERSON, + BUSINESS_POINT_OF_CONTACT, + TRUSTEE, + SETTLOR, + GENERAL_PARTNER, + /** + * An enum member indicating that [IndividualType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIRECTOR -> Value.DIRECTOR + CONTROL_PERSON -> Value.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Value.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Value.TRUSTEE + SETTLOR -> Value.SETTLOR + GENERAL_PARTNER -> Value.GENERAL_PARTNER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + DIRECTOR -> Known.DIRECTOR + CONTROL_PERSON -> Known.CONTROL_PERSON + BUSINESS_POINT_OF_CONTACT -> Known.BUSINESS_POINT_OF_CONTACT + TRUSTEE -> Known.TRUSTEE + SETTLOR -> Known.SETTLOR + GENERAL_PARTNER -> Known.GENERAL_PARTNER + else -> throw GridInvalidDataException("Unknown IndividualType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): IndividualType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IndividualType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") + @ExcludeMissing + line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") + @ExcludeMissing + city: JsonField = JsonMissing.of(), + @JsonProperty("line2") + @ExcludeMissing + line2: JsonField = JsonMissing.of(), + @JsonProperty("state") + @ExcludeMissing + state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + country, + line1, + postalCode, + city, + line2, + state, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficialOwner && + fullName == other.fullName && + individualType == other.individualType && + address == other.address && + birthDate == other.birthDate && + emailAddress == other.emailAddress && + nationality == other.nationality && + percentageOwnership == other.percentageOwnership && + phoneNumber == other.phoneNumber && + taxId == other.taxId && + title == other.title && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + fullName, + individualType, + address, + birthDate, + emailAddress, + nationality, + percentageOwnership, + phoneNumber, + taxId, + title, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BeneficialOwner{fullName=$fullName, individualType=$individualType, address=$address, birthDate=$birthDate, emailAddress=$emailAddress, nationality=$nationality, percentageOwnership=$percentageOwnership, phoneNumber=$phoneNumber, taxId=$taxId, title=$title, additionalProperties=$additionalProperties}" + } + + class BusinessInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val legalName: JsonField, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(legalName, registrationNumber, taxId, mutableMapOf()) + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun legalName(): String = legalName.getRequired("legalName") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun registrationNumber(): String? = registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("legalName") + @ExcludeMissing + fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BusinessInfo]. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BusinessInfo]. */ + class Builder internal constructor() { + + private var legalName: JsonField? = null + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(businessInfo: BusinessInfo) = apply { + legalName = businessInfo.legalName + registrationNumber = businessInfo.registrationNumber + taxId = businessInfo.taxId + additionalProperties = businessInfo.additionalProperties.toMutableMap() + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun legalName(legalName: JsonField) = apply { this.legalName = legalName } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BusinessInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BusinessInfo = + BusinessInfo( + checkRequired("legalName", legalName), + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BusinessInfo = apply { + if (validated) { + return@apply + } + + legalName() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (legalName.asKnown() == null) 0 else 1) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BusinessInfo && + legalName == other.legalName && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(legalName, registrationNumber, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BusinessInfo{legalName=$legalName, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + platformCustomerId == other.platformCustomerId && + umaAddress == other.umaAddress && + id == other.id && + createdAt == other.createdAt && + isDeleted == other.isDeleted && + kycStatus == other.kycStatus && + updatedAt == other.updatedAt && + customerType == other.customerType && + address == other.address && + beneficialOwners == other.beneficialOwners && + businessInfo == other.businessInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + platformCustomerId, + umaAddress, + id, + createdAt, + isDeleted, + kycStatus, + updatedAt, + customerType, + address, + beneficialOwners, + businessInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{platformCustomerId=$platformCustomerId, umaAddress=$umaAddress, id=$id, createdAt=$createdAt, isDeleted=$isDeleted, kycStatus=$kycStatus, updatedAt=$updatedAt, customerType=$customerType, address=$address, beneficialOwners=$beneficialOwners, businessInfo=$businessInfo, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParams.kt new file mode 100644 index 00000000..8789ddfb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParams.kt @@ -0,0 +1,192 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Retrieve the current status and results of a bulk customer import job. This endpoint can be used + * to track the progress of both CSV uploads. + * + * The response includes: + * - Overall job status + * - Progress statistics + * - Detailed error information for failed entries + * - Completion timestamp when finished + */ +class BulkGetJobStatusParams +private constructor( + private val jobId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun jobId(): String? = jobId + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): BulkGetJobStatusParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [BulkGetJobStatusParams]. */ + fun builder() = Builder() + } + + /** A builder for [BulkGetJobStatusParams]. */ + class Builder internal constructor() { + + private var jobId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(bulkGetJobStatusParams: BulkGetJobStatusParams) = apply { + jobId = bulkGetJobStatusParams.jobId + additionalHeaders = bulkGetJobStatusParams.additionalHeaders.toBuilder() + additionalQueryParams = bulkGetJobStatusParams.additionalQueryParams.toBuilder() + } + + fun jobId(jobId: String?) = apply { this.jobId = jobId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [BulkGetJobStatusParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BulkGetJobStatusParams = + BulkGetJobStatusParams(jobId, additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> jobId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BulkGetJobStatusParams && + jobId == other.jobId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(jobId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "BulkGetJobStatusParams{jobId=$jobId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponse.kt new file mode 100644 index 00000000..0039b758 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponse.kt @@ -0,0 +1,1126 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class BulkGetJobStatusResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val jobId: JsonField, + private val progress: JsonField, + private val status: JsonField, + private val completedAt: JsonField, + private val errors: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("jobId") @ExcludeMissing jobId: JsonField = JsonMissing.of(), + @JsonProperty("progress") @ExcludeMissing progress: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("completedAt") + @ExcludeMissing + completedAt: JsonField = JsonMissing.of(), + @JsonProperty("errors") @ExcludeMissing errors: JsonField> = JsonMissing.of(), + ) : this(jobId, progress, status, completedAt, errors, mutableMapOf()) + + /** + * Unique identifier for the bulk import job + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun jobId(): String = jobId.getRequired("jobId") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun progress(): Progress = progress.getRequired("progress") + + /** + * Current status of the job + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): Status = status.getRequired("status") + + /** + * Timestamp when the job completed (only present for COMPLETED or FAILED status) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun completedAt(): OffsetDateTime? = completedAt.getNullable("completedAt") + + /** + * Detailed error information for failed entries + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun errors(): List? = errors.getNullable("errors") + + /** + * Returns the raw JSON value of [jobId]. + * + * Unlike [jobId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("jobId") @ExcludeMissing fun _jobId(): JsonField = jobId + + /** + * Returns the raw JSON value of [progress]. + * + * Unlike [progress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("progress") @ExcludeMissing fun _progress(): JsonField = progress + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [completedAt]. + * + * Unlike [completedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("completedAt") + @ExcludeMissing + fun _completedAt(): JsonField = completedAt + + /** + * Returns the raw JSON value of [errors]. + * + * Unlike [errors], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("errors") @ExcludeMissing fun _errors(): JsonField> = errors + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BulkGetJobStatusResponse]. + * + * The following fields are required: + * ```kotlin + * .jobId() + * .progress() + * .status() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BulkGetJobStatusResponse]. */ + class Builder internal constructor() { + + private var jobId: JsonField? = null + private var progress: JsonField? = null + private var status: JsonField? = null + private var completedAt: JsonField = JsonMissing.of() + private var errors: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(bulkGetJobStatusResponse: BulkGetJobStatusResponse) = apply { + jobId = bulkGetJobStatusResponse.jobId + progress = bulkGetJobStatusResponse.progress + status = bulkGetJobStatusResponse.status + completedAt = bulkGetJobStatusResponse.completedAt + errors = bulkGetJobStatusResponse.errors.map { it.toMutableList() } + additionalProperties = bulkGetJobStatusResponse.additionalProperties.toMutableMap() + } + + /** Unique identifier for the bulk import job */ + fun jobId(jobId: String) = jobId(JsonField.of(jobId)) + + /** + * Sets [Builder.jobId] to an arbitrary JSON value. + * + * You should usually call [Builder.jobId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun jobId(jobId: JsonField) = apply { this.jobId = jobId } + + fun progress(progress: Progress) = progress(JsonField.of(progress)) + + /** + * Sets [Builder.progress] to an arbitrary JSON value. + * + * You should usually call [Builder.progress] with a well-typed [Progress] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun progress(progress: JsonField) = apply { this.progress = progress } + + /** Current status of the job */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** Timestamp when the job completed (only present for COMPLETED or FAILED status) */ + fun completedAt(completedAt: OffsetDateTime) = completedAt(JsonField.of(completedAt)) + + /** + * Sets [Builder.completedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.completedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun completedAt(completedAt: JsonField) = apply { + this.completedAt = completedAt + } + + /** Detailed error information for failed entries */ + fun errors(errors: List) = errors(JsonField.of(errors)) + + /** + * Sets [Builder.errors] to an arbitrary JSON value. + * + * You should usually call [Builder.errors] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun errors(errors: JsonField>) = apply { + this.errors = errors.map { it.toMutableList() } + } + + /** + * Adds a single [Error] to [errors]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addError(error: Error) = apply { + errors = + (errors ?: JsonField.of(mutableListOf())).also { + checkKnown("errors", it).add(error) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BulkGetJobStatusResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .jobId() + * .progress() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BulkGetJobStatusResponse = + BulkGetJobStatusResponse( + checkRequired("jobId", jobId), + checkRequired("progress", progress), + checkRequired("status", status), + completedAt, + (errors ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BulkGetJobStatusResponse = apply { + if (validated) { + return@apply + } + + jobId() + progress().validate() + status().validate() + completedAt() + errors()?.forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (jobId.asKnown() == null) 0 else 1) + + (progress.asKnown()?.validity() ?: 0) + + (status.asKnown()?.validity() ?: 0) + + (if (completedAt.asKnown() == null) 0 else 1) + + (errors.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + class Progress + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val failed: JsonField, + private val processed: JsonField, + private val successful: JsonField, + private val total: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("failed") @ExcludeMissing failed: JsonField = JsonMissing.of(), + @JsonProperty("processed") + @ExcludeMissing + processed: JsonField = JsonMissing.of(), + @JsonProperty("successful") + @ExcludeMissing + successful: JsonField = JsonMissing.of(), + @JsonProperty("total") @ExcludeMissing total: JsonField = JsonMissing.of(), + ) : this(failed, processed, successful, total, mutableMapOf()) + + /** + * Number of customers that failed to create + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun failed(): Long = failed.getRequired("failed") + + /** + * Number of customers processed so far + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun processed(): Long = processed.getRequired("processed") + + /** + * Number of customers successfully created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun successful(): Long = successful.getRequired("successful") + + /** + * Total number of customers to process + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun total(): Long = total.getRequired("total") + + /** + * Returns the raw JSON value of [failed]. + * + * Unlike [failed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("failed") @ExcludeMissing fun _failed(): JsonField = failed + + /** + * Returns the raw JSON value of [processed]. + * + * Unlike [processed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("processed") @ExcludeMissing fun _processed(): JsonField = processed + + /** + * Returns the raw JSON value of [successful]. + * + * Unlike [successful], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("successful") @ExcludeMissing fun _successful(): JsonField = successful + + /** + * Returns the raw JSON value of [total]. + * + * Unlike [total], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("total") @ExcludeMissing fun _total(): JsonField = total + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Progress]. + * + * The following fields are required: + * ```kotlin + * .failed() + * .processed() + * .successful() + * .total() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Progress]. */ + class Builder internal constructor() { + + private var failed: JsonField? = null + private var processed: JsonField? = null + private var successful: JsonField? = null + private var total: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(progress: Progress) = apply { + failed = progress.failed + processed = progress.processed + successful = progress.successful + total = progress.total + additionalProperties = progress.additionalProperties.toMutableMap() + } + + /** Number of customers that failed to create */ + fun failed(failed: Long) = failed(JsonField.of(failed)) + + /** + * Sets [Builder.failed] to an arbitrary JSON value. + * + * You should usually call [Builder.failed] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun failed(failed: JsonField) = apply { this.failed = failed } + + /** Number of customers processed so far */ + fun processed(processed: Long) = processed(JsonField.of(processed)) + + /** + * Sets [Builder.processed] to an arbitrary JSON value. + * + * You should usually call [Builder.processed] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun processed(processed: JsonField) = apply { this.processed = processed } + + /** Number of customers successfully created */ + fun successful(successful: Long) = successful(JsonField.of(successful)) + + /** + * Sets [Builder.successful] to an arbitrary JSON value. + * + * You should usually call [Builder.successful] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun successful(successful: JsonField) = apply { this.successful = successful } + + /** Total number of customers to process */ + fun total(total: Long) = total(JsonField.of(total)) + + /** + * Sets [Builder.total] to an arbitrary JSON value. + * + * You should usually call [Builder.total] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun total(total: JsonField) = apply { this.total = total } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Progress]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .failed() + * .processed() + * .successful() + * .total() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Progress = + Progress( + checkRequired("failed", failed), + checkRequired("processed", processed), + checkRequired("successful", successful), + checkRequired("total", total), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Progress = apply { + if (validated) { + return@apply + } + + failed() + processed() + successful() + total() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (failed.asKnown() == null) 0 else 1) + + (if (processed.asKnown() == null) 0 else 1) + + (if (successful.asKnown() == null) 0 else 1) + + (if (total.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Progress && + failed == other.failed && + processed == other.processed && + successful == other.successful && + total == other.total && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(failed, processed, successful, total, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Progress{failed=$failed, processed=$processed, successful=$successful, total=$total, additionalProperties=$additionalProperties}" + } + + /** Current status of the job */ + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PENDING = of("PENDING") + + val PROCESSING = of("PROCESSING") + + val COMPLETED = of("COMPLETED") + + val FAILED = of("FAILED") + + fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + PENDING, + PROCESSING, + COMPLETED, + FAILED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PENDING, + PROCESSING, + COMPLETED, + FAILED, + /** An enum member indicating that [Status] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PENDING -> Value.PENDING + PROCESSING -> Value.PROCESSING + COMPLETED -> Value.COMPLETED + FAILED -> Value.FAILED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + PENDING -> Known.PENDING + PROCESSING -> Known.PROCESSING + COMPLETED -> Known.COMPLETED + FAILED -> Known.FAILED + else -> throw GridInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Error + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val correlationId: JsonField, + private val code: JsonField, + private val details: JsonField
, + private val message: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("correlationId") + @ExcludeMissing + correlationId: JsonField = JsonMissing.of(), + @JsonProperty("code") @ExcludeMissing code: JsonField = JsonMissing.of(), + @JsonProperty("details") @ExcludeMissing details: JsonField
= JsonMissing.of(), + @JsonProperty("message") @ExcludeMissing message: JsonField = JsonMissing.of(), + ) : this(correlationId, code, details, message, mutableMapOf()) + + /** + * Platform customer ID or row number for the failed entry + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun correlationId(): String = correlationId.getRequired("correlationId") + + /** + * Error code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun code(): String? = code.getNullable("code") + + /** + * Additional error details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun details(): Details? = details.getNullable("details") + + /** + * Error message + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun message(): String? = message.getNullable("message") + + /** + * Returns the raw JSON value of [correlationId]. + * + * Unlike [correlationId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("correlationId") + @ExcludeMissing + fun _correlationId(): JsonField = correlationId + + /** + * Returns the raw JSON value of [code]. + * + * Unlike [code], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("code") @ExcludeMissing fun _code(): JsonField = code + + /** + * Returns the raw JSON value of [details]. + * + * Unlike [details], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("details") @ExcludeMissing fun _details(): JsonField
= details + + /** + * Returns the raw JSON value of [message]. + * + * Unlike [message], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Error]. + * + * The following fields are required: + * ```kotlin + * .correlationId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Error]. */ + class Builder internal constructor() { + + private var correlationId: JsonField? = null + private var code: JsonField = JsonMissing.of() + private var details: JsonField
= JsonMissing.of() + private var message: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(error: Error) = apply { + correlationId = error.correlationId + code = error.code + details = error.details + message = error.message + additionalProperties = error.additionalProperties.toMutableMap() + } + + /** Platform customer ID or row number for the failed entry */ + fun correlationId(correlationId: String) = correlationId(JsonField.of(correlationId)) + + /** + * Sets [Builder.correlationId] to an arbitrary JSON value. + * + * You should usually call [Builder.correlationId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun correlationId(correlationId: JsonField) = apply { + this.correlationId = correlationId + } + + /** Error code */ + fun code(code: String) = code(JsonField.of(code)) + + /** + * Sets [Builder.code] to an arbitrary JSON value. + * + * You should usually call [Builder.code] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun code(code: JsonField) = apply { this.code = code } + + /** Additional error details */ + fun details(details: Details) = details(JsonField.of(details)) + + /** + * Sets [Builder.details] to an arbitrary JSON value. + * + * You should usually call [Builder.details] with a well-typed [Details] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun details(details: JsonField
) = apply { this.details = details } + + /** Error message */ + fun message(message: String) = message(JsonField.of(message)) + + /** + * Sets [Builder.message] to an arbitrary JSON value. + * + * You should usually call [Builder.message] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun message(message: JsonField) = apply { this.message = message } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Error]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .correlationId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Error = + Error( + checkRequired("correlationId", correlationId), + code, + details, + message, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Error = apply { + if (validated) { + return@apply + } + + correlationId() + code() + details()?.validate() + message() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (correlationId.asKnown() == null) 0 else 1) + + (if (code.asKnown() == null) 0 else 1) + + (details.asKnown()?.validity() ?: 0) + + (if (message.asKnown() == null) 0 else 1) + + /** Additional error details */ + class Details + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Details]. */ + fun builder() = Builder() + } + + /** A builder for [Details]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(details: Details) = apply { + additionalProperties = details.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Details]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Details = Details(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Details = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Details && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Details{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Error && + correlationId == other.correlationId && + code == other.code && + details == other.details && + message == other.message && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(correlationId, code, details, message, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Error{correlationId=$correlationId, code=$code, details=$details, message=$message, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BulkGetJobStatusResponse && + jobId == other.jobId && + progress == other.progress && + status == other.status && + completedAt == other.completedAt && + errors == other.errors && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(jobId, progress, status, completedAt, errors, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BulkGetJobStatusResponse{jobId=$jobId, progress=$progress, status=$status, completedAt=$completedAt, errors=$errors, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParams.kt new file mode 100644 index 00000000..18b3bc64 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParams.kt @@ -0,0 +1,474 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.MultipartField +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.io.InputStream +import java.nio.file.Path +import java.util.Collections +import java.util.Objects +import kotlin.io.path.inputStream +import kotlin.io.path.name + +/** + * Upload a CSV file containing customer information for bulk creation. The CSV file should follow a + * specific format with required and optional columns based on customer type. + * + * ### CSV Format + * The CSV file should have the following columns: + * + * Required columns for all customers: + * - umaAddress: The customer's UMA address (e.g., $john.doe@uma.domain.com) + * - platformCustomerId: Your platform's unique identifier for the customer + * - customerType: Either "INDIVIDUAL" or "BUSINESS" + * + * Required columns for individual customers: + * - fullName: Individual's full name + * - birthDate: Date of birth in YYYY-MM-DD format + * - addressLine1: Street address line 1 + * - city: City + * - state: State/Province/Region + * - postalCode: Postal/ZIP code + * - country: Country code (ISO 3166-1 alpha-2) + * + * Required columns for business customers: + * - businessLegalName: Legal name of the business + * - addressLine1: Street address line 1 + * - city: City + * - state: State/Province/Region + * - postalCode: Postal/ZIP code + * - country: Country code (ISO 3166-1 alpha-2) + * + * Optional columns for all customers: + * - addressLine2: Street address line 2 + * - platformAccountId: Your platform's identifier for the bank account + * - description: Optional description for the customer + * + * Optional columns for individual customers: + * - email: Customer's email address + * + * Optional columns for business customers: + * - businessRegistrationNumber: Business registration number + * - businessTaxId: Tax identification number + * + * ### Example CSV + * + * ```csv + * umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName + * john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US + * acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US + * ``` + * + * The upload process is asynchronous and will return a job ID that can be used to track progress. + * You can monitor the job status using the `/customers/bulk/jobs/{jobId}` endpoint. + */ +class BulkUploadCsvParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * CSV file containing customer information + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun file(): InputStream = body.file() + + /** + * Returns the raw multipart value of [file]. + * + * Unlike [file], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _file(): MultipartField = body._file() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BulkUploadCsvParams]. + * + * The following fields are required: + * ```kotlin + * .file() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BulkUploadCsvParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(bulkUploadCsvParams: BulkUploadCsvParams) = apply { + body = bulkUploadCsvParams.body.toBuilder() + additionalHeaders = bulkUploadCsvParams.additionalHeaders.toBuilder() + additionalQueryParams = bulkUploadCsvParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [file] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** CSV file containing customer information */ + fun file(file: InputStream) = apply { body.file(file) } + + /** + * Sets [Builder.file] to an arbitrary multipart value. + * + * You should usually call [Builder.file] with a well-typed [InputStream] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun file(file: MultipartField) = apply { body.file(file) } + + /** CSV file containing customer information */ + fun file(file: ByteArray) = apply { body.file(file) } + + /** CSV file containing customer information */ + fun file(path: Path) = apply { body.file(path) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [BulkUploadCsvParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .file() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BulkUploadCsvParams = + BulkUploadCsvParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Map> = + (mapOf("file" to _file()) + + _additionalBodyProperties().mapValues { (_, value) -> MultipartField.of(value) }) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + private constructor( + private val file: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * CSV file containing customer information + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun file(): InputStream = file.value.getRequired("file") + + /** + * Returns the raw multipart value of [file]. + * + * Unlike [file], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("file") @ExcludeMissing fun _file(): MultipartField = file + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .file() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var file: MultipartField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + file = body.file + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** CSV file containing customer information */ + fun file(file: InputStream) = file(MultipartField.of(file)) + + /** + * Sets [Builder.file] to an arbitrary multipart value. + * + * You should usually call [Builder.file] with a well-typed [InputStream] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun file(file: MultipartField) = apply { this.file = file } + + /** CSV file containing customer information */ + fun file(file: ByteArray) = file(file.inputStream()) + + /** CSV file containing customer information */ + fun file(path: Path) = + file( + MultipartField.builder() + .value(path.inputStream()) + .filename(path.name) + .build() + ) + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .file() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body(checkRequired("file", file), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + file() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + file == other.file && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(file, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{file=$file, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BulkUploadCsvParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "BulkUploadCsvParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponse.kt new file mode 100644 index 00000000..6b0dc037 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponse.kt @@ -0,0 +1,330 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class BulkUploadCsvResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val jobId: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("jobId") @ExcludeMissing jobId: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(jobId, status, mutableMapOf()) + + /** + * Unique identifier for the bulk import job + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun jobId(): String = jobId.getRequired("jobId") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): Status = status.getRequired("status") + + /** + * Returns the raw JSON value of [jobId]. + * + * Unlike [jobId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("jobId") @ExcludeMissing fun _jobId(): JsonField = jobId + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BulkUploadCsvResponse]. + * + * The following fields are required: + * ```kotlin + * .jobId() + * .status() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BulkUploadCsvResponse]. */ + class Builder internal constructor() { + + private var jobId: JsonField? = null + private var status: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(bulkUploadCsvResponse: BulkUploadCsvResponse) = apply { + jobId = bulkUploadCsvResponse.jobId + status = bulkUploadCsvResponse.status + additionalProperties = bulkUploadCsvResponse.additionalProperties.toMutableMap() + } + + /** Unique identifier for the bulk import job */ + fun jobId(jobId: String) = jobId(JsonField.of(jobId)) + + /** + * Sets [Builder.jobId] to an arbitrary JSON value. + * + * You should usually call [Builder.jobId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun jobId(jobId: JsonField) = apply { this.jobId = jobId } + + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BulkUploadCsvResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .jobId() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BulkUploadCsvResponse = + BulkUploadCsvResponse( + checkRequired("jobId", jobId), + checkRequired("status", status), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BulkUploadCsvResponse = apply { + if (validated) { + return@apply + } + + jobId() + status().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (jobId.asKnown() == null) 0 else 1) + (status.asKnown()?.validity() ?: 0) + + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PENDING = of("PENDING") + + val PROCESSING = of("PROCESSING") + + fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + PENDING, + PROCESSING, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PENDING, + PROCESSING, + /** An enum member indicating that [Status] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PENDING -> Value.PENDING + PROCESSING -> Value.PROCESSING + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + PENDING -> Known.PENDING + PROCESSING -> Known.PROCESSING + else -> throw GridInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BulkUploadCsvResponse && + jobId == other.jobId && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(jobId, status, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BulkUploadCsvResponse{jobId=$jobId, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOf.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOf.kt new file mode 100644 index 00000000..dfb5995a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOf.kt @@ -0,0 +1,1556 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.util.Collections +import java.util.Objects + +@JsonDeserialize(using = BeneficiaryOneOf.Deserializer::class) +@JsonSerialize(using = BeneficiaryOneOf.Serializer::class) +class BeneficiaryOneOf +private constructor( + private val individual: Individual? = null, + private val business: Business? = null, + private val _json: JsonValue? = null, +) { + + fun individual(): Individual? = individual + + fun business(): Business? = business + + fun isIndividual(): Boolean = individual != null + + fun isBusiness(): Boolean = business != null + + fun asIndividual(): Individual = individual.getOrThrow("individual") + + fun asBusiness(): Business = business.getOrThrow("business") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + individual != null -> visitor.visitIndividual(individual) + business != null -> visitor.visitBusiness(business) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): BeneficiaryOneOf = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) { + individual.validate() + } + + override fun visitBusiness(business: Business) { + business.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitIndividual(individual: Individual) = individual.validity() + + override fun visitBusiness(business: Business) = business.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BeneficiaryOneOf && + individual == other.individual && + business == other.business + } + + override fun hashCode(): Int = Objects.hash(individual, business) + + override fun toString(): String = + when { + individual != null -> "BeneficiaryOneOf{individual=$individual}" + business != null -> "BeneficiaryOneOf{business=$business}" + _json != null -> "BeneficiaryOneOf{_unknown=$_json}" + else -> throw IllegalStateException("Invalid BeneficiaryOneOf") + } + + companion object { + + fun ofIndividual(individual: Individual) = BeneficiaryOneOf(individual = individual) + + fun ofBusiness(business: Business) = BeneficiaryOneOf(business = business) + } + + /** + * An interface that defines how to map each variant of [BeneficiaryOneOf] to a value of type + * [T]. + */ + interface Visitor { + + fun visitIndividual(individual: Individual): T + + fun visitBusiness(business: Business): T + + /** + * Maps an unknown variant of [BeneficiaryOneOf] to a value of type [T]. + * + * An instance of [BeneficiaryOneOf] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is unaware + * of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown BeneficiaryOneOf: $json") + } + } + + internal class Deserializer : BaseDeserializer(BeneficiaryOneOf::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): BeneficiaryOneOf { + val json = JsonValue.fromJsonNode(node) + val beneficiaryType = json.asObject()?.get("beneficiaryType")?.asString() + + when (beneficiaryType) { + "INDIVIDUAL" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + BeneficiaryOneOf(individual = it, _json = json) + } ?: BeneficiaryOneOf(_json = json) + } + "BUSINESS" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + BeneficiaryOneOf(business = it, _json = json) + } ?: BeneficiaryOneOf(_json = json) + } + } + + return BeneficiaryOneOf(_json = json) + } + } + + internal class Serializer : BaseSerializer(BeneficiaryOneOf::class) { + + override fun serialize( + value: BeneficiaryOneOf, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.individual != null -> generator.writeObject(value.individual) + value.business != null -> generator.writeObject(value.business) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid BeneficiaryOneOf") + } + } + } + + class Individual + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val beneficiaryType: JsonValue, + private val birthDate: JsonField, + private val fullName: JsonField, + private val nationality: JsonField, + private val address: JsonField
, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("beneficiaryType") + @ExcludeMissing + beneficiaryType: JsonValue = JsonMissing.of(), + @JsonProperty("birthDate") + @ExcludeMissing + birthDate: JsonField = JsonMissing.of(), + @JsonProperty("fullName") + @ExcludeMissing + fullName: JsonField = JsonMissing.of(), + @JsonProperty("nationality") + @ExcludeMissing + nationality: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + ) : this(beneficiaryType, birthDate, fullName, nationality, address, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("INDIVIDUAL") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("beneficiaryType") + @ExcludeMissing + fun _beneficiaryType(): JsonValue = beneficiaryType + + /** + * Date of birth in ISO 8601 format (YYYY-MM-DD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun birthDate(): LocalDate = birthDate.getRequired("birthDate") + + /** + * Individual's full name + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun fullName(): String = fullName.getRequired("fullName") + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun nationality(): String = nationality.getRequired("nationality") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Returns the raw JSON value of [birthDate]. + * + * Unlike [birthDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("birthDate") + @ExcludeMissing + fun _birthDate(): JsonField = birthDate + + /** + * Returns the raw JSON value of [fullName]. + * + * Unlike [fullName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fullName") @ExcludeMissing fun _fullName(): JsonField = fullName + + /** + * Returns the raw JSON value of [nationality]. + * + * Unlike [nationality], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nationality") + @ExcludeMissing + fun _nationality(): JsonField = nationality + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Individual]. + * + * The following fields are required: + * ```kotlin + * .birthDate() + * .fullName() + * .nationality() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Individual]. */ + class Builder internal constructor() { + + private var beneficiaryType: JsonValue = JsonValue.from("INDIVIDUAL") + private var birthDate: JsonField? = null + private var fullName: JsonField? = null + private var nationality: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(individual: Individual) = apply { + beneficiaryType = individual.beneficiaryType + birthDate = individual.birthDate + fullName = individual.fullName + nationality = individual.nationality + address = individual.address + additionalProperties = individual.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("INDIVIDUAL") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun beneficiaryType(beneficiaryType: JsonValue) = apply { + this.beneficiaryType = beneficiaryType + } + + /** Date of birth in ISO 8601 format (YYYY-MM-DD) */ + fun birthDate(birthDate: LocalDate) = birthDate(JsonField.of(birthDate)) + + /** + * Sets [Builder.birthDate] to an arbitrary JSON value. + * + * You should usually call [Builder.birthDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun birthDate(birthDate: JsonField) = apply { this.birthDate = birthDate } + + /** Individual's full name */ + fun fullName(fullName: String) = fullName(JsonField.of(fullName)) + + /** + * Sets [Builder.fullName] to an arbitrary JSON value. + * + * You should usually call [Builder.fullName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fullName(fullName: JsonField) = apply { this.fullName = fullName } + + /** Country code (ISO 3166-1 alpha-2) */ + fun nationality(nationality: String) = nationality(JsonField.of(nationality)) + + /** + * Sets [Builder.nationality] to an arbitrary JSON value. + * + * You should usually call [Builder.nationality] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nationality(nationality: JsonField) = apply { + this.nationality = nationality + } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Individual]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .birthDate() + * .fullName() + * .nationality() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Individual = + Individual( + beneficiaryType, + checkRequired("birthDate", birthDate), + checkRequired("fullName", fullName), + checkRequired("nationality", nationality), + address, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Individual = apply { + if (validated) { + return@apply + } + + _beneficiaryType().let { + if (it != JsonValue.from("INDIVIDUAL")) { + throw GridInvalidDataException("'beneficiaryType' is invalid, received $it") + } + } + birthDate() + fullName() + nationality() + address()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + beneficiaryType.let { if (it == JsonValue.from("INDIVIDUAL")) 1 else 0 } + + (if (birthDate.asKnown() == null) 0 else 1) + + (if (fullName.asKnown() == null) 0 else 1) + + (if (nationality.asKnown() == null) 0 else 1) + + (address.asKnown()?.validity() ?: 0) + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Individual && + beneficiaryType == other.beneficiaryType && + birthDate == other.birthDate && + fullName == other.fullName && + nationality == other.nationality && + address == other.address && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + beneficiaryType, + birthDate, + fullName, + nationality, + address, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Individual{beneficiaryType=$beneficiaryType, birthDate=$birthDate, fullName=$fullName, nationality=$nationality, address=$address, additionalProperties=$additionalProperties}" + } + + class Business + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val beneficiaryType: JsonValue, + private val legalName: JsonField, + private val address: JsonField
, + private val registrationNumber: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("beneficiaryType") + @ExcludeMissing + beneficiaryType: JsonValue = JsonMissing.of(), + @JsonProperty("legalName") + @ExcludeMissing + legalName: JsonField = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField
= JsonMissing.of(), + @JsonProperty("registrationNumber") + @ExcludeMissing + registrationNumber: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(beneficiaryType, legalName, address, registrationNumber, taxId, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("BUSINESS") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("beneficiaryType") + @ExcludeMissing + fun _beneficiaryType(): JsonValue = beneficiaryType + + /** + * Legal name of the business + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun legalName(): String = legalName.getRequired("legalName") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun address(): Address? = address.getNullable("address") + + /** + * Business registration number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun registrationNumber(): String? = registrationNumber.getNullable("registrationNumber") + + /** + * Tax identification number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun taxId(): String? = taxId.getNullable("taxId") + + /** + * Returns the raw JSON value of [legalName]. + * + * Unlike [legalName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("legalName") @ExcludeMissing fun _legalName(): JsonField = legalName + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField
= address + + /** + * Returns the raw JSON value of [registrationNumber]. + * + * Unlike [registrationNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("registrationNumber") + @ExcludeMissing + fun _registrationNumber(): JsonField = registrationNumber + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Business]. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Business]. */ + class Builder internal constructor() { + + private var beneficiaryType: JsonValue = JsonValue.from("BUSINESS") + private var legalName: JsonField? = null + private var address: JsonField
= JsonMissing.of() + private var registrationNumber: JsonField = JsonMissing.of() + private var taxId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(business: Business) = apply { + beneficiaryType = business.beneficiaryType + legalName = business.legalName + address = business.address + registrationNumber = business.registrationNumber + taxId = business.taxId + additionalProperties = business.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("BUSINESS") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun beneficiaryType(beneficiaryType: JsonValue) = apply { + this.beneficiaryType = beneficiaryType + } + + /** Legal name of the business */ + fun legalName(legalName: String) = legalName(JsonField.of(legalName)) + + /** + * Sets [Builder.legalName] to an arbitrary JSON value. + * + * You should usually call [Builder.legalName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun legalName(legalName: JsonField) = apply { this.legalName = legalName } + + fun address(address: Address) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [Address] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField
) = apply { this.address = address } + + /** Business registration number */ + fun registrationNumber(registrationNumber: String) = + registrationNumber(JsonField.of(registrationNumber)) + + /** + * Sets [Builder.registrationNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.registrationNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun registrationNumber(registrationNumber: JsonField) = apply { + this.registrationNumber = registrationNumber + } + + /** Tax identification number */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Business]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .legalName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Business = + Business( + beneficiaryType, + checkRequired("legalName", legalName), + address, + registrationNumber, + taxId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Business = apply { + if (validated) { + return@apply + } + + _beneficiaryType().let { + if (it != JsonValue.from("BUSINESS")) { + throw GridInvalidDataException("'beneficiaryType' is invalid, received $it") + } + } + legalName() + address()?.validate() + registrationNumber() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + beneficiaryType.let { if (it == JsonValue.from("BUSINESS")) 1 else 0 } + + (if (legalName.asKnown() == null) 0 else 1) + + (address.asKnown()?.validity() ?: 0) + + (if (registrationNumber.asKnown() == null) 0 else 1) + + (if (taxId.asKnown() == null) 0 else 1) + + class Address + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val country: JsonField, + private val line1: JsonField, + private val postalCode: JsonField, + private val city: JsonField, + private val line2: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("country") + @ExcludeMissing + country: JsonField = JsonMissing.of(), + @JsonProperty("line1") @ExcludeMissing line1: JsonField = JsonMissing.of(), + @JsonProperty("postalCode") + @ExcludeMissing + postalCode: JsonField = JsonMissing.of(), + @JsonProperty("city") @ExcludeMissing city: JsonField = JsonMissing.of(), + @JsonProperty("line2") @ExcludeMissing line2: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(country, line1, postalCode, city, line2, state, mutableMapOf()) + + /** + * Country code (ISO 3166-1 alpha-2) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun country(): String = country.getRequired("country") + + /** + * Street address line 1 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun line1(): String = line1.getRequired("line1") + + /** + * Postal/ZIP code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun postalCode(): String = postalCode.getRequired("postalCode") + + /** + * City + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun city(): String? = city.getNullable("city") + + /** + * Street address line 2 + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun line2(): String? = line2.getNullable("line2") + + /** + * State/Province/Region + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): String? = state.getNullable("state") + + /** + * Returns the raw JSON value of [country]. + * + * Unlike [country], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("country") @ExcludeMissing fun _country(): JsonField = country + + /** + * Returns the raw JSON value of [line1]. + * + * Unlike [line1], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line1") @ExcludeMissing fun _line1(): JsonField = line1 + + /** + * Returns the raw JSON value of [postalCode]. + * + * Unlike [postalCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("postalCode") + @ExcludeMissing + fun _postalCode(): JsonField = postalCode + + /** + * Returns the raw JSON value of [city]. + * + * Unlike [city], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("city") @ExcludeMissing fun _city(): JsonField = city + + /** + * Returns the raw JSON value of [line2]. + * + * Unlike [line2], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line2") @ExcludeMissing fun _line2(): JsonField = line2 + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Address]. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Address]. */ + class Builder internal constructor() { + + private var country: JsonField? = null + private var line1: JsonField? = null + private var postalCode: JsonField? = null + private var city: JsonField = JsonMissing.of() + private var line2: JsonField = JsonMissing.of() + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(address: Address) = apply { + country = address.country + line1 = address.line1 + postalCode = address.postalCode + city = address.city + line2 = address.line2 + state = address.state + additionalProperties = address.additionalProperties.toMutableMap() + } + + /** Country code (ISO 3166-1 alpha-2) */ + fun country(country: String) = country(JsonField.of(country)) + + /** + * Sets [Builder.country] to an arbitrary JSON value. + * + * You should usually call [Builder.country] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun country(country: JsonField) = apply { this.country = country } + + /** Street address line 1 */ + fun line1(line1: String) = line1(JsonField.of(line1)) + + /** + * Sets [Builder.line1] to an arbitrary JSON value. + * + * You should usually call [Builder.line1] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line1(line1: JsonField) = apply { this.line1 = line1 } + + /** Postal/ZIP code */ + fun postalCode(postalCode: String) = postalCode(JsonField.of(postalCode)) + + /** + * Sets [Builder.postalCode] to an arbitrary JSON value. + * + * You should usually call [Builder.postalCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun postalCode(postalCode: JsonField) = apply { + this.postalCode = postalCode + } + + /** City */ + fun city(city: String) = city(JsonField.of(city)) + + /** + * Sets [Builder.city] to an arbitrary JSON value. + * + * You should usually call [Builder.city] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun city(city: JsonField) = apply { this.city = city } + + /** Street address line 2 */ + fun line2(line2: String) = line2(JsonField.of(line2)) + + /** + * Sets [Builder.line2] to an arbitrary JSON value. + * + * You should usually call [Builder.line2] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun line2(line2: JsonField) = apply { this.line2 = line2 } + + /** State/Province/Region */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Address]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .country() + * .line1() + * .postalCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Address = + Address( + checkRequired("country", country), + checkRequired("line1", line1), + checkRequired("postalCode", postalCode), + city, + line2, + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Address = apply { + if (validated) { + return@apply + } + + country() + line1() + postalCode() + city() + line2() + state() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (country.asKnown() == null) 0 else 1) + + (if (line1.asKnown() == null) 0 else 1) + + (if (postalCode.asKnown() == null) 0 else 1) + + (if (city.asKnown() == null) 0 else 1) + + (if (line2.asKnown() == null) 0 else 1) + + (if (state.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Address && + country == other.country && + line1 == other.line1 && + postalCode == other.postalCode && + city == other.city && + line2 == other.line2 && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(country, line1, postalCode, city, line2, state, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Address{country=$country, line1=$line1, postalCode=$postalCode, city=$city, line2=$line2, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Business && + beneficiaryType == other.beneficiaryType && + legalName == other.legalName && + address == other.address && + registrationNumber == other.registrationNumber && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + beneficiaryType, + legalName, + address, + registrationNumber, + taxId, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Business{beneficiaryType=$beneficiaryType, legalName=$legalName, address=$address, registrationNumber=$registrationNumber, taxId=$taxId, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccount.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccount.kt new file mode 100644 index 00000000..7a2fca84 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccount.kt @@ -0,0 +1,729 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class ExternalAccount +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val accountInfo: JsonField, + private val currency: JsonField, + private val status: JsonField, + private val customerId: JsonField, + private val defaultUmaDepositAccount: JsonField, + private val platformAccountId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("accountInfo") + @ExcludeMissing + accountInfo: JsonField = JsonMissing.of(), + @JsonProperty("currency") @ExcludeMissing currency: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("defaultUmaDepositAccount") + @ExcludeMissing + defaultUmaDepositAccount: JsonField = JsonMissing.of(), + @JsonProperty("platformAccountId") + @ExcludeMissing + platformAccountId: JsonField = JsonMissing.of(), + ) : this( + id, + accountInfo, + currency, + status, + customerId, + defaultUmaDepositAccount, + platformAccountId, + mutableMapOf(), + ) + + /** + * The system generated identifier of this account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountInfo(): ExternalAccountInfoOneOf = accountInfo.getRequired("accountInfo") + + /** + * The ISO 4217 currency code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun currency(): String = currency.getRequired("currency") + + /** + * Status of the external account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): Status = status.getRequired("status") + + /** + * The customer this account is tied to, or null if the account is on behalf of the platform. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * Whether this account is the default UMA deposit account for the customer. If true, incoming + * UMA payments to this customer's UMA address will be automatically deposited into this account + * instead of the primary internal account. False if not provided. Note that at most, one + * external account can be set as the default UMA deposit account for a customer. If there is no + * default UMA deposit account, incoming UMA payments will be deposited into the primary + * internal account for the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun defaultUmaDepositAccount(): Boolean? = + defaultUmaDepositAccount.getNullable("defaultUmaDepositAccount") + + /** + * Optional platform-specific identifier for this account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun platformAccountId(): String? = platformAccountId.getNullable("platformAccountId") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [accountInfo]. + * + * Unlike [accountInfo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountInfo") + @ExcludeMissing + fun _accountInfo(): JsonField = accountInfo + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [defaultUmaDepositAccount]. + * + * Unlike [defaultUmaDepositAccount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("defaultUmaDepositAccount") + @ExcludeMissing + fun _defaultUmaDepositAccount(): JsonField = defaultUmaDepositAccount + + /** + * Returns the raw JSON value of [platformAccountId]. + * + * Unlike [platformAccountId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformAccountId") + @ExcludeMissing + fun _platformAccountId(): JsonField = platformAccountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccount]. + * + * The following fields are required: + * ```kotlin + * .id() + * .accountInfo() + * .currency() + * .status() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccount]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var accountInfo: JsonField? = null + private var currency: JsonField? = null + private var status: JsonField? = null + private var customerId: JsonField = JsonMissing.of() + private var defaultUmaDepositAccount: JsonField = JsonMissing.of() + private var platformAccountId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(externalAccount: ExternalAccount) = apply { + id = externalAccount.id + accountInfo = externalAccount.accountInfo + currency = externalAccount.currency + status = externalAccount.status + customerId = externalAccount.customerId + defaultUmaDepositAccount = externalAccount.defaultUmaDepositAccount + platformAccountId = externalAccount.platformAccountId + additionalProperties = externalAccount.additionalProperties.toMutableMap() + } + + /** The system generated identifier of this account */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun accountInfo(accountInfo: ExternalAccountInfoOneOf) = + accountInfo(JsonField.of(accountInfo)) + + /** + * Sets [Builder.accountInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.accountInfo] with a well-typed + * [ExternalAccountInfoOneOf] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun accountInfo(accountInfo: JsonField) = apply { + this.accountInfo = accountInfo + } + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofUsAccount(usAccount)`. + */ + fun accountInfo(usAccount: ExternalAccountInfoOneOf.UsAccount) = + accountInfo(ExternalAccountInfoOneOf.ofUsAccount(usAccount)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofClabe(clabe)`. */ + fun accountInfo(clabe: ExternalAccountInfoOneOf.Clabe) = + accountInfo(ExternalAccountInfoOneOf.ofClabe(clabe)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofPix(pix)`. */ + fun accountInfo(pix: ExternalAccountInfoOneOf.Pix) = + accountInfo(ExternalAccountInfoOneOf.ofPix(pix)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofIban(iban)`. */ + fun accountInfo(iban: ExternalAccountInfoOneOf.Iban) = + accountInfo(ExternalAccountInfoOneOf.ofIban(iban)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofUpi(upi)`. */ + fun accountInfo(upi: ExternalAccountInfoOneOf.Upi) = + accountInfo(ExternalAccountInfoOneOf.ofUpi(upi)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofNgnAccount(ngnAccount)`. + */ + fun accountInfo(ngnAccount: ExternalAccountInfoOneOf.NgnAccount) = + accountInfo(ExternalAccountInfoOneOf.ofNgnAccount(ngnAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofCadAccount(cadAccount)`. + */ + fun accountInfo(cadAccount: ExternalAccountInfoOneOf.CadAccount) = + accountInfo(ExternalAccountInfoOneOf.ofCadAccount(cadAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofGbpAccount(gbpAccount)`. + */ + fun accountInfo(gbpAccount: ExternalAccountInfoOneOf.GbpAccount) = + accountInfo(ExternalAccountInfoOneOf.ofGbpAccount(gbpAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofPhpAccount(phpAccount)`. + */ + fun accountInfo(phpAccount: ExternalAccountInfoOneOf.PhpAccount) = + accountInfo(ExternalAccountInfoOneOf.ofPhpAccount(phpAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofSgdAccount(sgdAccount)`. + */ + fun accountInfo(sgdAccount: ExternalAccountInfoOneOf.SgdAccount) = + accountInfo(ExternalAccountInfoOneOf.ofSgdAccount(sgdAccount)) + + /** + * Alias for calling [accountInfo] with + * `ExternalAccountInfoOneOf.ofSparkWallet(sparkWallet)`. + */ + fun accountInfo(sparkWallet: ExternalAccountInfoOneOf.SparkWallet) = + accountInfo(ExternalAccountInfoOneOf.ofSparkWallet(sparkWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.SparkWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun sparkWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.SparkWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofLightning(lightning)`. + */ + fun accountInfo(lightning: ExternalAccountInfoOneOf.Lightning) = + accountInfo(ExternalAccountInfoOneOf.ofLightning(lightning)) + + /** + * Alias for calling [accountInfo] with + * `ExternalAccountInfoOneOf.ofSolanaWallet(solanaWallet)`. + */ + fun accountInfo(solanaWallet: ExternalAccountInfoOneOf.SolanaWallet) = + accountInfo(ExternalAccountInfoOneOf.ofSolanaWallet(solanaWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.SolanaWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun solanaWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.SolanaWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofTronWallet(tronWallet)`. + */ + fun accountInfo(tronWallet: ExternalAccountInfoOneOf.TronWallet) = + accountInfo(ExternalAccountInfoOneOf.ofTronWallet(tronWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.TronWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun tronWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.TronWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with + * `ExternalAccountInfoOneOf.ofPolygonWallet(polygonWallet)`. + */ + fun accountInfo(polygonWallet: ExternalAccountInfoOneOf.PolygonWallet) = + accountInfo(ExternalAccountInfoOneOf.ofPolygonWallet(polygonWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.PolygonWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun polygonWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.PolygonWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofBaseWallet(baseWallet)`. + */ + fun accountInfo(baseWallet: ExternalAccountInfoOneOf.BaseWallet) = + accountInfo(ExternalAccountInfoOneOf.ofBaseWallet(baseWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.BaseWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun baseWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.BaseWallet.builder().address(address).build()) + + /** The ISO 4217 currency code */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + /** Status of the external account */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * The customer this account is tied to, or null if the account is on behalf of the + * platform. + */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** + * Whether this account is the default UMA deposit account for the customer. If true, + * incoming UMA payments to this customer's UMA address will be automatically deposited into + * this account instead of the primary internal account. False if not provided. Note that at + * most, one external account can be set as the default UMA deposit account for a customer. + * If there is no default UMA deposit account, incoming UMA payments will be deposited into + * the primary internal account for the customer. + */ + fun defaultUmaDepositAccount(defaultUmaDepositAccount: Boolean) = + defaultUmaDepositAccount(JsonField.of(defaultUmaDepositAccount)) + + /** + * Sets [Builder.defaultUmaDepositAccount] to an arbitrary JSON value. + * + * You should usually call [Builder.defaultUmaDepositAccount] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun defaultUmaDepositAccount(defaultUmaDepositAccount: JsonField) = apply { + this.defaultUmaDepositAccount = defaultUmaDepositAccount + } + + /** Optional platform-specific identifier for this account */ + fun platformAccountId(platformAccountId: String) = + platformAccountId(JsonField.of(platformAccountId)) + + /** + * Sets [Builder.platformAccountId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformAccountId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformAccountId(platformAccountId: JsonField) = apply { + this.platformAccountId = platformAccountId + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ExternalAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .id() + * .accountInfo() + * .currency() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccount = + ExternalAccount( + checkRequired("id", id), + checkRequired("accountInfo", accountInfo), + checkRequired("currency", currency), + checkRequired("status", status), + customerId, + defaultUmaDepositAccount, + platformAccountId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ExternalAccount = apply { + if (validated) { + return@apply + } + + id() + accountInfo().validate() + currency() + status().validate() + customerId() + defaultUmaDepositAccount() + platformAccountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (accountInfo.asKnown()?.validity() ?: 0) + + (if (currency.asKnown() == null) 0 else 1) + + (status.asKnown()?.validity() ?: 0) + + (if (customerId.asKnown() == null) 0 else 1) + + (if (defaultUmaDepositAccount.asKnown() == null) 0 else 1) + + (if (platformAccountId.asKnown() == null) 0 else 1) + + /** Status of the external account */ + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PENDING = of("PENDING") + + val ACTIVE = of("ACTIVE") + + val UNDER_REVIEW = of("UNDER_REVIEW") + + val INACTIVE = of("INACTIVE") + + fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + PENDING, + ACTIVE, + UNDER_REVIEW, + INACTIVE, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PENDING, + ACTIVE, + UNDER_REVIEW, + INACTIVE, + /** An enum member indicating that [Status] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PENDING -> Value.PENDING + ACTIVE -> Value.ACTIVE + UNDER_REVIEW -> Value.UNDER_REVIEW + INACTIVE -> Value.INACTIVE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + PENDING -> Known.PENDING + ACTIVE -> Known.ACTIVE + UNDER_REVIEW -> Known.UNDER_REVIEW + INACTIVE -> Known.INACTIVE + else -> throw GridInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccount && + id == other.id && + accountInfo == other.accountInfo && + currency == other.currency && + status == other.status && + customerId == other.customerId && + defaultUmaDepositAccount == other.defaultUmaDepositAccount && + platformAccountId == other.platformAccountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + accountInfo, + currency, + status, + customerId, + defaultUmaDepositAccount, + platformAccountId, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ExternalAccount{id=$id, accountInfo=$accountInfo, currency=$currency, status=$status, customerId=$customerId, defaultUmaDepositAccount=$defaultUmaDepositAccount, platformAccountId=$platformAccountId, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreate.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreate.kt new file mode 100644 index 00000000..c7071a57 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreate.kt @@ -0,0 +1,522 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class ExternalAccountCreate +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val accountInfo: JsonField, + private val currency: JsonField, + private val customerId: JsonField, + private val defaultUmaDepositAccount: JsonField, + private val platformAccountId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("accountInfo") + @ExcludeMissing + accountInfo: JsonField = JsonMissing.of(), + @JsonProperty("currency") @ExcludeMissing currency: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("defaultUmaDepositAccount") + @ExcludeMissing + defaultUmaDepositAccount: JsonField = JsonMissing.of(), + @JsonProperty("platformAccountId") + @ExcludeMissing + platformAccountId: JsonField = JsonMissing.of(), + ) : this( + accountInfo, + currency, + customerId, + defaultUmaDepositAccount, + platformAccountId, + mutableMapOf(), + ) + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountInfo(): ExternalAccountInfoOneOf = accountInfo.getRequired("accountInfo") + + /** + * The ISO 4217 currency code + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun currency(): String = currency.getRequired("currency") + + /** + * The ID of the customer for whom to create the external account. If not provided, the external + * account will be created on behalf of the platform. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * Whether to set the external account as the default UMA deposit account. When set to true, + * incoming payments to this customer's UMA address will be automatically deposited into this + * external account. False if not provided. Note that only one external account can be set as + * the default UMA deposit account for a customer, so if there is already a default UMA deposit + * account, this will override the existing default UMA deposit account. If there is no default + * UMA deposit account, incoming UMA payments will be deposited into the primary internal + * account for the customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun defaultUmaDepositAccount(): Boolean? = + defaultUmaDepositAccount.getNullable("defaultUmaDepositAccount") + + /** + * Your platform's identifier for the account in your system. This can be used to reference the + * account by your own identifier. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun platformAccountId(): String? = platformAccountId.getNullable("platformAccountId") + + /** + * Returns the raw JSON value of [accountInfo]. + * + * Unlike [accountInfo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountInfo") + @ExcludeMissing + fun _accountInfo(): JsonField = accountInfo + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [defaultUmaDepositAccount]. + * + * Unlike [defaultUmaDepositAccount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("defaultUmaDepositAccount") + @ExcludeMissing + fun _defaultUmaDepositAccount(): JsonField = defaultUmaDepositAccount + + /** + * Returns the raw JSON value of [platformAccountId]. + * + * Unlike [platformAccountId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformAccountId") + @ExcludeMissing + fun _platformAccountId(): JsonField = platformAccountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountCreate]. + * + * The following fields are required: + * ```kotlin + * .accountInfo() + * .currency() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountCreate]. */ + class Builder internal constructor() { + + private var accountInfo: JsonField? = null + private var currency: JsonField? = null + private var customerId: JsonField = JsonMissing.of() + private var defaultUmaDepositAccount: JsonField = JsonMissing.of() + private var platformAccountId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(externalAccountCreate: ExternalAccountCreate) = apply { + accountInfo = externalAccountCreate.accountInfo + currency = externalAccountCreate.currency + customerId = externalAccountCreate.customerId + defaultUmaDepositAccount = externalAccountCreate.defaultUmaDepositAccount + platformAccountId = externalAccountCreate.platformAccountId + additionalProperties = externalAccountCreate.additionalProperties.toMutableMap() + } + + fun accountInfo(accountInfo: ExternalAccountInfoOneOf) = + accountInfo(JsonField.of(accountInfo)) + + /** + * Sets [Builder.accountInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.accountInfo] with a well-typed + * [ExternalAccountInfoOneOf] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun accountInfo(accountInfo: JsonField) = apply { + this.accountInfo = accountInfo + } + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofUsAccount(usAccount)`. + */ + fun accountInfo(usAccount: ExternalAccountInfoOneOf.UsAccount) = + accountInfo(ExternalAccountInfoOneOf.ofUsAccount(usAccount)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofClabe(clabe)`. */ + fun accountInfo(clabe: ExternalAccountInfoOneOf.Clabe) = + accountInfo(ExternalAccountInfoOneOf.ofClabe(clabe)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofPix(pix)`. */ + fun accountInfo(pix: ExternalAccountInfoOneOf.Pix) = + accountInfo(ExternalAccountInfoOneOf.ofPix(pix)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofIban(iban)`. */ + fun accountInfo(iban: ExternalAccountInfoOneOf.Iban) = + accountInfo(ExternalAccountInfoOneOf.ofIban(iban)) + + /** Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofUpi(upi)`. */ + fun accountInfo(upi: ExternalAccountInfoOneOf.Upi) = + accountInfo(ExternalAccountInfoOneOf.ofUpi(upi)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofNgnAccount(ngnAccount)`. + */ + fun accountInfo(ngnAccount: ExternalAccountInfoOneOf.NgnAccount) = + accountInfo(ExternalAccountInfoOneOf.ofNgnAccount(ngnAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofCadAccount(cadAccount)`. + */ + fun accountInfo(cadAccount: ExternalAccountInfoOneOf.CadAccount) = + accountInfo(ExternalAccountInfoOneOf.ofCadAccount(cadAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofGbpAccount(gbpAccount)`. + */ + fun accountInfo(gbpAccount: ExternalAccountInfoOneOf.GbpAccount) = + accountInfo(ExternalAccountInfoOneOf.ofGbpAccount(gbpAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofPhpAccount(phpAccount)`. + */ + fun accountInfo(phpAccount: ExternalAccountInfoOneOf.PhpAccount) = + accountInfo(ExternalAccountInfoOneOf.ofPhpAccount(phpAccount)) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofSgdAccount(sgdAccount)`. + */ + fun accountInfo(sgdAccount: ExternalAccountInfoOneOf.SgdAccount) = + accountInfo(ExternalAccountInfoOneOf.ofSgdAccount(sgdAccount)) + + /** + * Alias for calling [accountInfo] with + * `ExternalAccountInfoOneOf.ofSparkWallet(sparkWallet)`. + */ + fun accountInfo(sparkWallet: ExternalAccountInfoOneOf.SparkWallet) = + accountInfo(ExternalAccountInfoOneOf.ofSparkWallet(sparkWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.SparkWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun sparkWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.SparkWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofLightning(lightning)`. + */ + fun accountInfo(lightning: ExternalAccountInfoOneOf.Lightning) = + accountInfo(ExternalAccountInfoOneOf.ofLightning(lightning)) + + /** + * Alias for calling [accountInfo] with + * `ExternalAccountInfoOneOf.ofSolanaWallet(solanaWallet)`. + */ + fun accountInfo(solanaWallet: ExternalAccountInfoOneOf.SolanaWallet) = + accountInfo(ExternalAccountInfoOneOf.ofSolanaWallet(solanaWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.SolanaWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun solanaWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.SolanaWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofTronWallet(tronWallet)`. + */ + fun accountInfo(tronWallet: ExternalAccountInfoOneOf.TronWallet) = + accountInfo(ExternalAccountInfoOneOf.ofTronWallet(tronWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.TronWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun tronWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.TronWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with + * `ExternalAccountInfoOneOf.ofPolygonWallet(polygonWallet)`. + */ + fun accountInfo(polygonWallet: ExternalAccountInfoOneOf.PolygonWallet) = + accountInfo(ExternalAccountInfoOneOf.ofPolygonWallet(polygonWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.PolygonWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun polygonWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.PolygonWallet.builder().address(address).build()) + + /** + * Alias for calling [accountInfo] with `ExternalAccountInfoOneOf.ofBaseWallet(baseWallet)`. + */ + fun accountInfo(baseWallet: ExternalAccountInfoOneOf.BaseWallet) = + accountInfo(ExternalAccountInfoOneOf.ofBaseWallet(baseWallet)) + + /** + * Alias for calling [accountInfo] with the following: + * ```kotlin + * ExternalAccountInfoOneOf.BaseWallet.builder() + * .address(address) + * .build() + * ``` + */ + fun baseWalletAccountInfo(address: String) = + accountInfo(ExternalAccountInfoOneOf.BaseWallet.builder().address(address).build()) + + /** The ISO 4217 currency code */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + /** + * The ID of the customer for whom to create the external account. If not provided, the + * external account will be created on behalf of the platform. + */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** + * Whether to set the external account as the default UMA deposit account. When set to true, + * incoming payments to this customer's UMA address will be automatically deposited into + * this external account. False if not provided. Note that only one external account can be + * set as the default UMA deposit account for a customer, so if there is already a default + * UMA deposit account, this will override the existing default UMA deposit account. If + * there is no default UMA deposit account, incoming UMA payments will be deposited into the + * primary internal account for the customer. + */ + fun defaultUmaDepositAccount(defaultUmaDepositAccount: Boolean) = + defaultUmaDepositAccount(JsonField.of(defaultUmaDepositAccount)) + + /** + * Sets [Builder.defaultUmaDepositAccount] to an arbitrary JSON value. + * + * You should usually call [Builder.defaultUmaDepositAccount] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun defaultUmaDepositAccount(defaultUmaDepositAccount: JsonField) = apply { + this.defaultUmaDepositAccount = defaultUmaDepositAccount + } + + /** + * Your platform's identifier for the account in your system. This can be used to reference + * the account by your own identifier. + */ + fun platformAccountId(platformAccountId: String) = + platformAccountId(JsonField.of(platformAccountId)) + + /** + * Sets [Builder.platformAccountId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformAccountId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformAccountId(platformAccountId: JsonField) = apply { + this.platformAccountId = platformAccountId + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ExternalAccountCreate]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountInfo() + * .currency() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountCreate = + ExternalAccountCreate( + checkRequired("accountInfo", accountInfo), + checkRequired("currency", currency), + customerId, + defaultUmaDepositAccount, + platformAccountId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ExternalAccountCreate = apply { + if (validated) { + return@apply + } + + accountInfo().validate() + currency() + customerId() + defaultUmaDepositAccount() + platformAccountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountInfo.asKnown()?.validity() ?: 0) + + (if (currency.asKnown() == null) 0 else 1) + + (if (customerId.asKnown() == null) 0 else 1) + + (if (defaultUmaDepositAccount.asKnown() == null) 0 else 1) + + (if (platformAccountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountCreate && + accountInfo == other.accountInfo && + currency == other.currency && + customerId == other.customerId && + defaultUmaDepositAccount == other.defaultUmaDepositAccount && + platformAccountId == other.platformAccountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountInfo, + currency, + customerId, + defaultUmaDepositAccount, + platformAccountId, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ExternalAccountCreate{accountInfo=$accountInfo, currency=$currency, customerId=$customerId, defaultUmaDepositAccount=$defaultUmaDepositAccount, platformAccountId=$platformAccountId, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParams.kt new file mode 100644 index 00000000..d0b7aec9 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParams.kt @@ -0,0 +1,215 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Register a new external bank account for a customer. + * + * **Sandbox Testing:** In sandbox mode, use these account number patterns to test different + * transfer scenarios. These patterns should be used with the primary alias, address, or identifier + * of whatever account type you're testing. For example, the US account number, a CLABE, an IBAN, a + * spark wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ +class ExternalAccountCreateParams +private constructor( + private val externalAccountCreate: ExternalAccountCreate, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun externalAccountCreate(): ExternalAccountCreate = externalAccountCreate + + fun _additionalBodyProperties(): Map = + externalAccountCreate._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountCreateParams]. + * + * The following fields are required: + * ```kotlin + * .externalAccountCreate() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountCreateParams]. */ + class Builder internal constructor() { + + private var externalAccountCreate: ExternalAccountCreate? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(externalAccountCreateParams: ExternalAccountCreateParams) = apply { + externalAccountCreate = externalAccountCreateParams.externalAccountCreate + additionalHeaders = externalAccountCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = externalAccountCreateParams.additionalQueryParams.toBuilder() + } + + fun externalAccountCreate(externalAccountCreate: ExternalAccountCreate) = apply { + this.externalAccountCreate = externalAccountCreate + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExternalAccountCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .externalAccountCreate() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountCreateParams = + ExternalAccountCreateParams( + checkRequired("externalAccountCreate", externalAccountCreate), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): ExternalAccountCreate = externalAccountCreate + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountCreateParams && + externalAccountCreate == other.externalAccountCreate && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(externalAccountCreate, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ExternalAccountCreateParams{externalAccountCreate=$externalAccountCreate, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOf.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOf.kt new file mode 100644 index 00000000..4ee2b680 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOf.kt @@ -0,0 +1,5532 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +@JsonDeserialize(using = ExternalAccountInfoOneOf.Deserializer::class) +@JsonSerialize(using = ExternalAccountInfoOneOf.Serializer::class) +class ExternalAccountInfoOneOf +private constructor( + private val usAccount: UsAccount? = null, + private val clabe: Clabe? = null, + private val pix: Pix? = null, + private val iban: Iban? = null, + private val upi: Upi? = null, + private val ngnAccount: NgnAccount? = null, + private val cadAccount: CadAccount? = null, + private val gbpAccount: GbpAccount? = null, + private val phpAccount: PhpAccount? = null, + private val sgdAccount: SgdAccount? = null, + private val sparkWallet: SparkWallet? = null, + private val lightning: Lightning? = null, + private val solanaWallet: SolanaWallet? = null, + private val tronWallet: TronWallet? = null, + private val polygonWallet: PolygonWallet? = null, + private val baseWallet: BaseWallet? = null, + private val _json: JsonValue? = null, +) { + + fun usAccount(): UsAccount? = usAccount + + fun clabe(): Clabe? = clabe + + fun pix(): Pix? = pix + + fun iban(): Iban? = iban + + fun upi(): Upi? = upi + + fun ngnAccount(): NgnAccount? = ngnAccount + + fun cadAccount(): CadAccount? = cadAccount + + fun gbpAccount(): GbpAccount? = gbpAccount + + fun phpAccount(): PhpAccount? = phpAccount + + fun sgdAccount(): SgdAccount? = sgdAccount + + fun sparkWallet(): SparkWallet? = sparkWallet + + fun lightning(): Lightning? = lightning + + fun solanaWallet(): SolanaWallet? = solanaWallet + + fun tronWallet(): TronWallet? = tronWallet + + fun polygonWallet(): PolygonWallet? = polygonWallet + + fun baseWallet(): BaseWallet? = baseWallet + + fun isUsAccount(): Boolean = usAccount != null + + fun isClabe(): Boolean = clabe != null + + fun isPix(): Boolean = pix != null + + fun isIban(): Boolean = iban != null + + fun isUpi(): Boolean = upi != null + + fun isNgnAccount(): Boolean = ngnAccount != null + + fun isCadAccount(): Boolean = cadAccount != null + + fun isGbpAccount(): Boolean = gbpAccount != null + + fun isPhpAccount(): Boolean = phpAccount != null + + fun isSgdAccount(): Boolean = sgdAccount != null + + fun isSparkWallet(): Boolean = sparkWallet != null + + fun isLightning(): Boolean = lightning != null + + fun isSolanaWallet(): Boolean = solanaWallet != null + + fun isTronWallet(): Boolean = tronWallet != null + + fun isPolygonWallet(): Boolean = polygonWallet != null + + fun isBaseWallet(): Boolean = baseWallet != null + + fun asUsAccount(): UsAccount = usAccount.getOrThrow("usAccount") + + fun asClabe(): Clabe = clabe.getOrThrow("clabe") + + fun asPix(): Pix = pix.getOrThrow("pix") + + fun asIban(): Iban = iban.getOrThrow("iban") + + fun asUpi(): Upi = upi.getOrThrow("upi") + + fun asNgnAccount(): NgnAccount = ngnAccount.getOrThrow("ngnAccount") + + fun asCadAccount(): CadAccount = cadAccount.getOrThrow("cadAccount") + + fun asGbpAccount(): GbpAccount = gbpAccount.getOrThrow("gbpAccount") + + fun asPhpAccount(): PhpAccount = phpAccount.getOrThrow("phpAccount") + + fun asSgdAccount(): SgdAccount = sgdAccount.getOrThrow("sgdAccount") + + fun asSparkWallet(): SparkWallet = sparkWallet.getOrThrow("sparkWallet") + + fun asLightning(): Lightning = lightning.getOrThrow("lightning") + + fun asSolanaWallet(): SolanaWallet = solanaWallet.getOrThrow("solanaWallet") + + fun asTronWallet(): TronWallet = tronWallet.getOrThrow("tronWallet") + + fun asPolygonWallet(): PolygonWallet = polygonWallet.getOrThrow("polygonWallet") + + fun asBaseWallet(): BaseWallet = baseWallet.getOrThrow("baseWallet") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + usAccount != null -> visitor.visitUsAccount(usAccount) + clabe != null -> visitor.visitClabe(clabe) + pix != null -> visitor.visitPix(pix) + iban != null -> visitor.visitIban(iban) + upi != null -> visitor.visitUpi(upi) + ngnAccount != null -> visitor.visitNgnAccount(ngnAccount) + cadAccount != null -> visitor.visitCadAccount(cadAccount) + gbpAccount != null -> visitor.visitGbpAccount(gbpAccount) + phpAccount != null -> visitor.visitPhpAccount(phpAccount) + sgdAccount != null -> visitor.visitSgdAccount(sgdAccount) + sparkWallet != null -> visitor.visitSparkWallet(sparkWallet) + lightning != null -> visitor.visitLightning(lightning) + solanaWallet != null -> visitor.visitSolanaWallet(solanaWallet) + tronWallet != null -> visitor.visitTronWallet(tronWallet) + polygonWallet != null -> visitor.visitPolygonWallet(polygonWallet) + baseWallet != null -> visitor.visitBaseWallet(baseWallet) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ExternalAccountInfoOneOf = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitUsAccount(usAccount: UsAccount) { + usAccount.validate() + } + + override fun visitClabe(clabe: Clabe) { + clabe.validate() + } + + override fun visitPix(pix: Pix) { + pix.validate() + } + + override fun visitIban(iban: Iban) { + iban.validate() + } + + override fun visitUpi(upi: Upi) { + upi.validate() + } + + override fun visitNgnAccount(ngnAccount: NgnAccount) { + ngnAccount.validate() + } + + override fun visitCadAccount(cadAccount: CadAccount) { + cadAccount.validate() + } + + override fun visitGbpAccount(gbpAccount: GbpAccount) { + gbpAccount.validate() + } + + override fun visitPhpAccount(phpAccount: PhpAccount) { + phpAccount.validate() + } + + override fun visitSgdAccount(sgdAccount: SgdAccount) { + sgdAccount.validate() + } + + override fun visitSparkWallet(sparkWallet: SparkWallet) { + sparkWallet.validate() + } + + override fun visitLightning(lightning: Lightning) { + lightning.validate() + } + + override fun visitSolanaWallet(solanaWallet: SolanaWallet) { + solanaWallet.validate() + } + + override fun visitTronWallet(tronWallet: TronWallet) { + tronWallet.validate() + } + + override fun visitPolygonWallet(polygonWallet: PolygonWallet) { + polygonWallet.validate() + } + + override fun visitBaseWallet(baseWallet: BaseWallet) { + baseWallet.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitUsAccount(usAccount: UsAccount) = usAccount.validity() + + override fun visitClabe(clabe: Clabe) = clabe.validity() + + override fun visitPix(pix: Pix) = pix.validity() + + override fun visitIban(iban: Iban) = iban.validity() + + override fun visitUpi(upi: Upi) = upi.validity() + + override fun visitNgnAccount(ngnAccount: NgnAccount) = ngnAccount.validity() + + override fun visitCadAccount(cadAccount: CadAccount) = cadAccount.validity() + + override fun visitGbpAccount(gbpAccount: GbpAccount) = gbpAccount.validity() + + override fun visitPhpAccount(phpAccount: PhpAccount) = phpAccount.validity() + + override fun visitSgdAccount(sgdAccount: SgdAccount) = sgdAccount.validity() + + override fun visitSparkWallet(sparkWallet: SparkWallet) = sparkWallet.validity() + + override fun visitLightning(lightning: Lightning) = lightning.validity() + + override fun visitSolanaWallet(solanaWallet: SolanaWallet) = solanaWallet.validity() + + override fun visitTronWallet(tronWallet: TronWallet) = tronWallet.validity() + + override fun visitPolygonWallet(polygonWallet: PolygonWallet) = + polygonWallet.validity() + + override fun visitBaseWallet(baseWallet: BaseWallet) = baseWallet.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountInfoOneOf && + usAccount == other.usAccount && + clabe == other.clabe && + pix == other.pix && + iban == other.iban && + upi == other.upi && + ngnAccount == other.ngnAccount && + cadAccount == other.cadAccount && + gbpAccount == other.gbpAccount && + phpAccount == other.phpAccount && + sgdAccount == other.sgdAccount && + sparkWallet == other.sparkWallet && + lightning == other.lightning && + solanaWallet == other.solanaWallet && + tronWallet == other.tronWallet && + polygonWallet == other.polygonWallet && + baseWallet == other.baseWallet + } + + override fun hashCode(): Int = + Objects.hash( + usAccount, + clabe, + pix, + iban, + upi, + ngnAccount, + cadAccount, + gbpAccount, + phpAccount, + sgdAccount, + sparkWallet, + lightning, + solanaWallet, + tronWallet, + polygonWallet, + baseWallet, + ) + + override fun toString(): String = + when { + usAccount != null -> "ExternalAccountInfoOneOf{usAccount=$usAccount}" + clabe != null -> "ExternalAccountInfoOneOf{clabe=$clabe}" + pix != null -> "ExternalAccountInfoOneOf{pix=$pix}" + iban != null -> "ExternalAccountInfoOneOf{iban=$iban}" + upi != null -> "ExternalAccountInfoOneOf{upi=$upi}" + ngnAccount != null -> "ExternalAccountInfoOneOf{ngnAccount=$ngnAccount}" + cadAccount != null -> "ExternalAccountInfoOneOf{cadAccount=$cadAccount}" + gbpAccount != null -> "ExternalAccountInfoOneOf{gbpAccount=$gbpAccount}" + phpAccount != null -> "ExternalAccountInfoOneOf{phpAccount=$phpAccount}" + sgdAccount != null -> "ExternalAccountInfoOneOf{sgdAccount=$sgdAccount}" + sparkWallet != null -> "ExternalAccountInfoOneOf{sparkWallet=$sparkWallet}" + lightning != null -> "ExternalAccountInfoOneOf{lightning=$lightning}" + solanaWallet != null -> "ExternalAccountInfoOneOf{solanaWallet=$solanaWallet}" + tronWallet != null -> "ExternalAccountInfoOneOf{tronWallet=$tronWallet}" + polygonWallet != null -> "ExternalAccountInfoOneOf{polygonWallet=$polygonWallet}" + baseWallet != null -> "ExternalAccountInfoOneOf{baseWallet=$baseWallet}" + _json != null -> "ExternalAccountInfoOneOf{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ExternalAccountInfoOneOf") + } + + companion object { + + fun ofUsAccount(usAccount: UsAccount) = ExternalAccountInfoOneOf(usAccount = usAccount) + + fun ofClabe(clabe: Clabe) = ExternalAccountInfoOneOf(clabe = clabe) + + fun ofPix(pix: Pix) = ExternalAccountInfoOneOf(pix = pix) + + fun ofIban(iban: Iban) = ExternalAccountInfoOneOf(iban = iban) + + fun ofUpi(upi: Upi) = ExternalAccountInfoOneOf(upi = upi) + + fun ofNgnAccount(ngnAccount: NgnAccount) = ExternalAccountInfoOneOf(ngnAccount = ngnAccount) + + fun ofCadAccount(cadAccount: CadAccount) = ExternalAccountInfoOneOf(cadAccount = cadAccount) + + fun ofGbpAccount(gbpAccount: GbpAccount) = ExternalAccountInfoOneOf(gbpAccount = gbpAccount) + + fun ofPhpAccount(phpAccount: PhpAccount) = ExternalAccountInfoOneOf(phpAccount = phpAccount) + + fun ofSgdAccount(sgdAccount: SgdAccount) = ExternalAccountInfoOneOf(sgdAccount = sgdAccount) + + fun ofSparkWallet(sparkWallet: SparkWallet) = + ExternalAccountInfoOneOf(sparkWallet = sparkWallet) + + fun ofLightning(lightning: Lightning) = ExternalAccountInfoOneOf(lightning = lightning) + + fun ofSolanaWallet(solanaWallet: SolanaWallet) = + ExternalAccountInfoOneOf(solanaWallet = solanaWallet) + + fun ofTronWallet(tronWallet: TronWallet) = ExternalAccountInfoOneOf(tronWallet = tronWallet) + + fun ofPolygonWallet(polygonWallet: PolygonWallet) = + ExternalAccountInfoOneOf(polygonWallet = polygonWallet) + + fun ofBaseWallet(baseWallet: BaseWallet) = ExternalAccountInfoOneOf(baseWallet = baseWallet) + } + + /** + * An interface that defines how to map each variant of [ExternalAccountInfoOneOf] to a value of + * type [T]. + */ + interface Visitor { + + fun visitUsAccount(usAccount: UsAccount): T + + fun visitClabe(clabe: Clabe): T + + fun visitPix(pix: Pix): T + + fun visitIban(iban: Iban): T + + fun visitUpi(upi: Upi): T + + fun visitNgnAccount(ngnAccount: NgnAccount): T + + fun visitCadAccount(cadAccount: CadAccount): T + + fun visitGbpAccount(gbpAccount: GbpAccount): T + + fun visitPhpAccount(phpAccount: PhpAccount): T + + fun visitSgdAccount(sgdAccount: SgdAccount): T + + fun visitSparkWallet(sparkWallet: SparkWallet): T + + fun visitLightning(lightning: Lightning): T + + fun visitSolanaWallet(solanaWallet: SolanaWallet): T + + fun visitTronWallet(tronWallet: TronWallet): T + + fun visitPolygonWallet(polygonWallet: PolygonWallet): T + + fun visitBaseWallet(baseWallet: BaseWallet): T + + /** + * Maps an unknown variant of [ExternalAccountInfoOneOf] to a value of type [T]. + * + * An instance of [ExternalAccountInfoOneOf] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown ExternalAccountInfoOneOf: $json") + } + } + + internal class Deserializer : + BaseDeserializer(ExternalAccountInfoOneOf::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ExternalAccountInfoOneOf { + val json = JsonValue.fromJsonNode(node) + val accountType = json.asObject()?.get("accountType")?.asString() + + when (accountType) { + "US_ACCOUNT" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(usAccount = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "CLABE" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(clabe = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "PIX" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(pix = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "IBAN" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(iban = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "UPI" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(upi = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "NGN_ACCOUNT" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(ngnAccount = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "CAD_ACCOUNT" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(cadAccount = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "GBP_ACCOUNT" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(gbpAccount = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "PHP_ACCOUNT" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(phpAccount = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "SGD_ACCOUNT" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(sgdAccount = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "SPARK_WALLET" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(sparkWallet = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "LIGHTNING" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(lightning = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "SOLANA_WALLET" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(solanaWallet = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "TRON_WALLET" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(tronWallet = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "POLYGON_WALLET" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(polygonWallet = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + "BASE_WALLET" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ExternalAccountInfoOneOf(baseWallet = it, _json = json) + } ?: ExternalAccountInfoOneOf(_json = json) + } + } + + return ExternalAccountInfoOneOf(_json = json) + } + } + + internal class Serializer : + BaseSerializer(ExternalAccountInfoOneOf::class) { + + override fun serialize( + value: ExternalAccountInfoOneOf, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.usAccount != null -> generator.writeObject(value.usAccount) + value.clabe != null -> generator.writeObject(value.clabe) + value.pix != null -> generator.writeObject(value.pix) + value.iban != null -> generator.writeObject(value.iban) + value.upi != null -> generator.writeObject(value.upi) + value.ngnAccount != null -> generator.writeObject(value.ngnAccount) + value.cadAccount != null -> generator.writeObject(value.cadAccount) + value.gbpAccount != null -> generator.writeObject(value.gbpAccount) + value.phpAccount != null -> generator.writeObject(value.phpAccount) + value.sgdAccount != null -> generator.writeObject(value.sgdAccount) + value.sparkWallet != null -> generator.writeObject(value.sparkWallet) + value.lightning != null -> generator.writeObject(value.lightning) + value.solanaWallet != null -> generator.writeObject(value.solanaWallet) + value.tronWallet != null -> generator.writeObject(value.tronWallet) + value.polygonWallet != null -> generator.writeObject(value.polygonWallet) + value.baseWallet != null -> generator.writeObject(value.baseWallet) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ExternalAccountInfoOneOf") + } + } + } + + class UsAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountCategory: JsonField, + private val accountNumber: JsonField, + private val accountType: JsonValue, + private val beneficiary: JsonField, + private val routingNumber: JsonField, + private val bankName: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountCategory") + @ExcludeMissing + accountCategory: JsonField = JsonMissing.of(), + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("routingNumber") + @ExcludeMissing + routingNumber: JsonField = JsonMissing.of(), + @JsonProperty("bankName") @ExcludeMissing bankName: JsonField = JsonMissing.of(), + ) : this( + accountCategory, + accountNumber, + accountType, + beneficiary, + routingNumber, + bankName, + mutableMapOf(), + ) + + /** + * Type of account (checking or savings) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountCategory(): AccountCategory = accountCategory.getRequired("accountCategory") + + /** + * US bank account number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("US_ACCOUNT") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * ACH routing number (9 digits) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun routingNumber(): String = routingNumber.getRequired("routingNumber") + + /** + * Name of the bank + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun bankName(): String? = bankName.getNullable("bankName") + + /** + * Returns the raw JSON value of [accountCategory]. + * + * Unlike [accountCategory], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountCategory") + @ExcludeMissing + fun _accountCategory(): JsonField = accountCategory + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [routingNumber]. + * + * Unlike [routingNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("routingNumber") + @ExcludeMissing + fun _routingNumber(): JsonField = routingNumber + + /** + * Returns the raw JSON value of [bankName]. + * + * Unlike [bankName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bankName") @ExcludeMissing fun _bankName(): JsonField = bankName + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UsAccount]. + * + * The following fields are required: + * ```kotlin + * .accountCategory() + * .accountNumber() + * .beneficiary() + * .routingNumber() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UsAccount]. */ + class Builder internal constructor() { + + private var accountCategory: JsonField? = null + private var accountNumber: JsonField? = null + private var accountType: JsonValue = JsonValue.from("US_ACCOUNT") + private var beneficiary: JsonField? = null + private var routingNumber: JsonField? = null + private var bankName: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(usAccount: UsAccount) = apply { + accountCategory = usAccount.accountCategory + accountNumber = usAccount.accountNumber + accountType = usAccount.accountType + beneficiary = usAccount.beneficiary + routingNumber = usAccount.routingNumber + bankName = usAccount.bankName + additionalProperties = usAccount.additionalProperties.toMutableMap() + } + + /** Type of account (checking or savings) */ + fun accountCategory(accountCategory: AccountCategory) = + accountCategory(JsonField.of(accountCategory)) + + /** + * Sets [Builder.accountCategory] to an arbitrary JSON value. + * + * You should usually call [Builder.accountCategory] with a well-typed [AccountCategory] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun accountCategory(accountCategory: JsonField) = apply { + this.accountCategory = accountCategory + } + + /** US bank account number */ + fun accountNumber(accountNumber: String) = accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("US_ACCOUNT") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** ACH routing number (9 digits) */ + fun routingNumber(routingNumber: String) = routingNumber(JsonField.of(routingNumber)) + + /** + * Sets [Builder.routingNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.routingNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun routingNumber(routingNumber: JsonField) = apply { + this.routingNumber = routingNumber + } + + /** Name of the bank */ + fun bankName(bankName: String) = bankName(JsonField.of(bankName)) + + /** + * Sets [Builder.bankName] to an arbitrary JSON value. + * + * You should usually call [Builder.bankName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bankName(bankName: JsonField) = apply { this.bankName = bankName } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UsAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountCategory() + * .accountNumber() + * .beneficiary() + * .routingNumber() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UsAccount = + UsAccount( + checkRequired("accountCategory", accountCategory), + checkRequired("accountNumber", accountNumber), + accountType, + checkRequired("beneficiary", beneficiary), + checkRequired("routingNumber", routingNumber), + bankName, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UsAccount = apply { + if (validated) { + return@apply + } + + accountCategory().validate() + accountNumber() + _accountType().let { + if (it != JsonValue.from("US_ACCOUNT")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + beneficiary().validate() + routingNumber() + bankName() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountCategory.asKnown()?.validity() ?: 0) + + (if (accountNumber.asKnown() == null) 0 else 1) + + accountType.let { if (it == JsonValue.from("US_ACCOUNT")) 1 else 0 } + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (routingNumber.asKnown() == null) 0 else 1) + + (if (bankName.asKnown() == null) 0 else 1) + + /** Type of account (checking or savings) */ + class AccountCategory + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val CHECKING = of("CHECKING") + + val SAVINGS = of("SAVINGS") + + fun of(value: String) = AccountCategory(JsonField.of(value)) + } + + /** An enum containing [AccountCategory]'s known values. */ + enum class Known { + CHECKING, + SAVINGS, + } + + /** + * An enum containing [AccountCategory]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountCategory] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CHECKING, + SAVINGS, + /** + * An enum member indicating that [AccountCategory] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CHECKING -> Value.CHECKING + SAVINGS -> Value.SAVINGS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CHECKING -> Known.CHECKING + SAVINGS -> Known.SAVINGS + else -> throw GridInvalidDataException("Unknown AccountCategory: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountCategory = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountCategory && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UsAccount && + accountCategory == other.accountCategory && + accountNumber == other.accountNumber && + accountType == other.accountType && + beneficiary == other.beneficiary && + routingNumber == other.routingNumber && + bankName == other.bankName && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountCategory, + accountNumber, + accountType, + beneficiary, + routingNumber, + bankName, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UsAccount{accountCategory=$accountCategory, accountNumber=$accountNumber, accountType=$accountType, beneficiary=$beneficiary, routingNumber=$routingNumber, bankName=$bankName, additionalProperties=$additionalProperties}" + } + + class Clabe + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val beneficiary: JsonField, + private val clabeNumber: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("clabeNumber") + @ExcludeMissing + clabeNumber: JsonField = JsonMissing.of(), + ) : this(accountType, beneficiary, clabeNumber, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("CLABE") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * 18-digit CLABE number (Mexican banking standard) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun clabeNumber(): String = clabeNumber.getRequired("clabeNumber") + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [clabeNumber]. + * + * Unlike [clabeNumber], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("clabeNumber") + @ExcludeMissing + fun _clabeNumber(): JsonField = clabeNumber + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Clabe]. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .clabeNumber() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Clabe]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("CLABE") + private var beneficiary: JsonField? = null + private var clabeNumber: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(clabe: Clabe) = apply { + accountType = clabe.accountType + beneficiary = clabe.beneficiary + clabeNumber = clabe.clabeNumber + additionalProperties = clabe.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("CLABE") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** 18-digit CLABE number (Mexican banking standard) */ + fun clabeNumber(clabeNumber: String) = clabeNumber(JsonField.of(clabeNumber)) + + /** + * Sets [Builder.clabeNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.clabeNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun clabeNumber(clabeNumber: JsonField) = apply { + this.clabeNumber = clabeNumber + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Clabe]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .clabeNumber() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Clabe = + Clabe( + accountType, + checkRequired("beneficiary", beneficiary), + checkRequired("clabeNumber", clabeNumber), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Clabe = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("CLABE")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + beneficiary().validate() + clabeNumber() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("CLABE")) 1 else 0 } + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (clabeNumber.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Clabe && + accountType == other.accountType && + beneficiary == other.beneficiary && + clabeNumber == other.clabeNumber && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, beneficiary, clabeNumber, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Clabe{accountType=$accountType, beneficiary=$beneficiary, clabeNumber=$clabeNumber, additionalProperties=$additionalProperties}" + } + + class Pix + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val beneficiary: JsonField, + private val pixKey: JsonField, + private val pixKeyType: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("pixKey") @ExcludeMissing pixKey: JsonField = JsonMissing.of(), + @JsonProperty("pixKeyType") + @ExcludeMissing + pixKeyType: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(accountType, beneficiary, pixKey, pixKeyType, taxId, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("PIX") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * PIX key for Brazilian instant payments + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun pixKey(): String = pixKey.getRequired("pixKey") + + /** + * Type of PIX key being used + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun pixKeyType(): PixKeyType = pixKeyType.getRequired("pixKeyType") + + /** + * Tax ID of the account holder + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun taxId(): String = taxId.getRequired("taxId") + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [pixKey]. + * + * Unlike [pixKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pixKey") @ExcludeMissing fun _pixKey(): JsonField = pixKey + + /** + * Returns the raw JSON value of [pixKeyType]. + * + * Unlike [pixKeyType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pixKeyType") + @ExcludeMissing + fun _pixKeyType(): JsonField = pixKeyType + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Pix]. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .pixKey() + * .pixKeyType() + * .taxId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Pix]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("PIX") + private var beneficiary: JsonField? = null + private var pixKey: JsonField? = null + private var pixKeyType: JsonField? = null + private var taxId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(pix: Pix) = apply { + accountType = pix.accountType + beneficiary = pix.beneficiary + pixKey = pix.pixKey + pixKeyType = pix.pixKeyType + taxId = pix.taxId + additionalProperties = pix.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("PIX") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** PIX key for Brazilian instant payments */ + fun pixKey(pixKey: String) = pixKey(JsonField.of(pixKey)) + + /** + * Sets [Builder.pixKey] to an arbitrary JSON value. + * + * You should usually call [Builder.pixKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pixKey(pixKey: JsonField) = apply { this.pixKey = pixKey } + + /** Type of PIX key being used */ + fun pixKeyType(pixKeyType: PixKeyType) = pixKeyType(JsonField.of(pixKeyType)) + + /** + * Sets [Builder.pixKeyType] to an arbitrary JSON value. + * + * You should usually call [Builder.pixKeyType] with a well-typed [PixKeyType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pixKeyType(pixKeyType: JsonField) = apply { + this.pixKeyType = pixKeyType + } + + /** Tax ID of the account holder */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Pix]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .pixKey() + * .pixKeyType() + * .taxId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Pix = + Pix( + accountType, + checkRequired("beneficiary", beneficiary), + checkRequired("pixKey", pixKey), + checkRequired("pixKeyType", pixKeyType), + checkRequired("taxId", taxId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Pix = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("PIX")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + beneficiary().validate() + pixKey() + pixKeyType().validate() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("PIX")) 1 else 0 } + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (pixKey.asKnown() == null) 0 else 1) + + (pixKeyType.asKnown()?.validity() ?: 0) + + (if (taxId.asKnown() == null) 0 else 1) + + /** Type of PIX key being used */ + class PixKeyType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val CPF = of("CPF") + + val CNPJ = of("CNPJ") + + val EMAIL = of("EMAIL") + + val PHONE = of("PHONE") + + val RANDOM = of("RANDOM") + + fun of(value: String) = PixKeyType(JsonField.of(value)) + } + + /** An enum containing [PixKeyType]'s known values. */ + enum class Known { + CPF, + CNPJ, + EMAIL, + PHONE, + RANDOM, + } + + /** + * An enum containing [PixKeyType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [PixKeyType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CPF, + CNPJ, + EMAIL, + PHONE, + RANDOM, + /** + * An enum member indicating that [PixKeyType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CPF -> Value.CPF + CNPJ -> Value.CNPJ + EMAIL -> Value.EMAIL + PHONE -> Value.PHONE + RANDOM -> Value.RANDOM + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CPF -> Known.CPF + CNPJ -> Known.CNPJ + EMAIL -> Known.EMAIL + PHONE -> Known.PHONE + RANDOM -> Known.RANDOM + else -> throw GridInvalidDataException("Unknown PixKeyType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): PixKeyType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PixKeyType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Pix && + accountType == other.accountType && + beneficiary == other.beneficiary && + pixKey == other.pixKey && + pixKeyType == other.pixKeyType && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, beneficiary, pixKey, pixKeyType, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Pix{accountType=$accountType, beneficiary=$beneficiary, pixKey=$pixKey, pixKeyType=$pixKeyType, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + class Iban + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val beneficiary: JsonField, + private val iban: JsonField, + private val swiftBic: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("iban") @ExcludeMissing iban: JsonField = JsonMissing.of(), + @JsonProperty("swiftBic") @ExcludeMissing swiftBic: JsonField = JsonMissing.of(), + ) : this(accountType, beneficiary, iban, swiftBic, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("IBAN") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * International Bank Account Number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun iban(): String = iban.getRequired("iban") + + /** + * SWIFT/BIC code (8 or 11 characters) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun swiftBic(): String = swiftBic.getRequired("swiftBic") + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [iban]. + * + * Unlike [iban], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("iban") @ExcludeMissing fun _iban(): JsonField = iban + + /** + * Returns the raw JSON value of [swiftBic]. + * + * Unlike [swiftBic], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("swiftBic") @ExcludeMissing fun _swiftBic(): JsonField = swiftBic + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Iban]. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .iban() + * .swiftBic() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Iban]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("IBAN") + private var beneficiary: JsonField? = null + private var iban: JsonField? = null + private var swiftBic: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(iban: Iban) = apply { + accountType = iban.accountType + beneficiary = iban.beneficiary + this.iban = iban.iban + swiftBic = iban.swiftBic + additionalProperties = iban.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("IBAN") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** International Bank Account Number */ + fun iban(iban: String) = iban(JsonField.of(iban)) + + /** + * Sets [Builder.iban] to an arbitrary JSON value. + * + * You should usually call [Builder.iban] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun iban(iban: JsonField) = apply { this.iban = iban } + + /** SWIFT/BIC code (8 or 11 characters) */ + fun swiftBic(swiftBic: String) = swiftBic(JsonField.of(swiftBic)) + + /** + * Sets [Builder.swiftBic] to an arbitrary JSON value. + * + * You should usually call [Builder.swiftBic] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun swiftBic(swiftBic: JsonField) = apply { this.swiftBic = swiftBic } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Iban]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .iban() + * .swiftBic() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Iban = + Iban( + accountType, + checkRequired("beneficiary", beneficiary), + checkRequired("iban", iban), + checkRequired("swiftBic", swiftBic), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Iban = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("IBAN")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + beneficiary().validate() + iban() + swiftBic() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("IBAN")) 1 else 0 } + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (iban.asKnown() == null) 0 else 1) + + (if (swiftBic.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Iban && + accountType == other.accountType && + beneficiary == other.beneficiary && + iban == other.iban && + swiftBic == other.swiftBic && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, beneficiary, iban, swiftBic, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Iban{accountType=$accountType, beneficiary=$beneficiary, iban=$iban, swiftBic=$swiftBic, additionalProperties=$additionalProperties}" + } + + class Upi + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val beneficiary: JsonField, + private val vpa: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("vpa") @ExcludeMissing vpa: JsonField = JsonMissing.of(), + ) : this(accountType, beneficiary, vpa, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("UPI") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * Virtual Payment Address for UPI payments + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun vpa(): String = vpa.getRequired("vpa") + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [vpa]. + * + * Unlike [vpa], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("vpa") @ExcludeMissing fun _vpa(): JsonField = vpa + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Upi]. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .vpa() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Upi]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("UPI") + private var beneficiary: JsonField? = null + private var vpa: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(upi: Upi) = apply { + accountType = upi.accountType + beneficiary = upi.beneficiary + vpa = upi.vpa + additionalProperties = upi.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("UPI") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** Virtual Payment Address for UPI payments */ + fun vpa(vpa: String) = vpa(JsonField.of(vpa)) + + /** + * Sets [Builder.vpa] to an arbitrary JSON value. + * + * You should usually call [Builder.vpa] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun vpa(vpa: JsonField) = apply { this.vpa = vpa } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Upi]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .beneficiary() + * .vpa() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Upi = + Upi( + accountType, + checkRequired("beneficiary", beneficiary), + checkRequired("vpa", vpa), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Upi = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("UPI")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + beneficiary().validate() + vpa() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("UPI")) 1 else 0 } + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (vpa.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Upi && + accountType == other.accountType && + beneficiary == other.beneficiary && + vpa == other.vpa && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, beneficiary, vpa, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Upi{accountType=$accountType, beneficiary=$beneficiary, vpa=$vpa, additionalProperties=$additionalProperties}" + } + + class NgnAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountNumber: JsonField, + private val accountType: JsonValue, + private val bankName: JsonField, + private val beneficiary: JsonField, + private val purposeOfPayment: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("bankName") + @ExcludeMissing + bankName: JsonField = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("purposeOfPayment") + @ExcludeMissing + purposeOfPayment: JsonField = JsonMissing.of(), + ) : this( + accountNumber, + accountType, + bankName, + beneficiary, + purposeOfPayment, + mutableMapOf(), + ) + + /** + * Nigerian bank account number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("NGN_ACCOUNT") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Name of the bank + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun bankName(): String = bankName.getRequired("bankName") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * Purpose of payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun purposeOfPayment(): PurposeOfPayment = purposeOfPayment.getRequired("purposeOfPayment") + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [bankName]. + * + * Unlike [bankName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bankName") @ExcludeMissing fun _bankName(): JsonField = bankName + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [purposeOfPayment]. + * + * Unlike [purposeOfPayment], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("purposeOfPayment") + @ExcludeMissing + fun _purposeOfPayment(): JsonField = purposeOfPayment + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [NgnAccount]. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankName() + * .beneficiary() + * .purposeOfPayment() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [NgnAccount]. */ + class Builder internal constructor() { + + private var accountNumber: JsonField? = null + private var accountType: JsonValue = JsonValue.from("NGN_ACCOUNT") + private var bankName: JsonField? = null + private var beneficiary: JsonField? = null + private var purposeOfPayment: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(ngnAccount: NgnAccount) = apply { + accountNumber = ngnAccount.accountNumber + accountType = ngnAccount.accountType + bankName = ngnAccount.bankName + beneficiary = ngnAccount.beneficiary + purposeOfPayment = ngnAccount.purposeOfPayment + additionalProperties = ngnAccount.additionalProperties.toMutableMap() + } + + /** Nigerian bank account number */ + fun accountNumber(accountNumber: String) = accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("NGN_ACCOUNT") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Name of the bank */ + fun bankName(bankName: String) = bankName(JsonField.of(bankName)) + + /** + * Sets [Builder.bankName] to an arbitrary JSON value. + * + * You should usually call [Builder.bankName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bankName(bankName: JsonField) = apply { this.bankName = bankName } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** Purpose of payment */ + fun purposeOfPayment(purposeOfPayment: PurposeOfPayment) = + purposeOfPayment(JsonField.of(purposeOfPayment)) + + /** + * Sets [Builder.purposeOfPayment] to an arbitrary JSON value. + * + * You should usually call [Builder.purposeOfPayment] with a well-typed + * [PurposeOfPayment] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun purposeOfPayment(purposeOfPayment: JsonField) = apply { + this.purposeOfPayment = purposeOfPayment + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [NgnAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankName() + * .beneficiary() + * .purposeOfPayment() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): NgnAccount = + NgnAccount( + checkRequired("accountNumber", accountNumber), + accountType, + checkRequired("bankName", bankName), + checkRequired("beneficiary", beneficiary), + checkRequired("purposeOfPayment", purposeOfPayment), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): NgnAccount = apply { + if (validated) { + return@apply + } + + accountNumber() + _accountType().let { + if (it != JsonValue.from("NGN_ACCOUNT")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + bankName() + beneficiary().validate() + purposeOfPayment().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountNumber.asKnown() == null) 0 else 1) + + accountType.let { if (it == JsonValue.from("NGN_ACCOUNT")) 1 else 0 } + + (if (bankName.asKnown() == null) 0 else 1) + + (beneficiary.asKnown()?.validity() ?: 0) + + (purposeOfPayment.asKnown()?.validity() ?: 0) + + /** Purpose of payment */ + class PurposeOfPayment + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val GIFT = of("GIFT") + + val SELF = of("SELF") + + val GOODS_OR_SERVICES = of("GOODS_OR_SERVICES") + + val EDUCATION = of("EDUCATION") + + val HEALTH_OR_MEDICAL = of("HEALTH_OR_MEDICAL") + + val REAL_ESTATE_PURCHASE = of("REAL_ESTATE_PURCHASE") + + val LOAN_PAYMENT = of("LOAN_PAYMENT") + + val TAX_PAYMENT = of("TAX_PAYMENT") + + val UTILITY_BILL = of("UTILITY_BILL") + + val DONATION = of("DONATION") + + val TRAVEL = of("TRAVEL") + + val OTHER = of("OTHER") + + fun of(value: String) = PurposeOfPayment(JsonField.of(value)) + } + + /** An enum containing [PurposeOfPayment]'s known values. */ + enum class Known { + GIFT, + SELF, + GOODS_OR_SERVICES, + EDUCATION, + HEALTH_OR_MEDICAL, + REAL_ESTATE_PURCHASE, + LOAN_PAYMENT, + TAX_PAYMENT, + UTILITY_BILL, + DONATION, + TRAVEL, + OTHER, + } + + /** + * An enum containing [PurposeOfPayment]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [PurposeOfPayment] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + GIFT, + SELF, + GOODS_OR_SERVICES, + EDUCATION, + HEALTH_OR_MEDICAL, + REAL_ESTATE_PURCHASE, + LOAN_PAYMENT, + TAX_PAYMENT, + UTILITY_BILL, + DONATION, + TRAVEL, + OTHER, + /** + * An enum member indicating that [PurposeOfPayment] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + GIFT -> Value.GIFT + SELF -> Value.SELF + GOODS_OR_SERVICES -> Value.GOODS_OR_SERVICES + EDUCATION -> Value.EDUCATION + HEALTH_OR_MEDICAL -> Value.HEALTH_OR_MEDICAL + REAL_ESTATE_PURCHASE -> Value.REAL_ESTATE_PURCHASE + LOAN_PAYMENT -> Value.LOAN_PAYMENT + TAX_PAYMENT -> Value.TAX_PAYMENT + UTILITY_BILL -> Value.UTILITY_BILL + DONATION -> Value.DONATION + TRAVEL -> Value.TRAVEL + OTHER -> Value.OTHER + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + GIFT -> Known.GIFT + SELF -> Known.SELF + GOODS_OR_SERVICES -> Known.GOODS_OR_SERVICES + EDUCATION -> Known.EDUCATION + HEALTH_OR_MEDICAL -> Known.HEALTH_OR_MEDICAL + REAL_ESTATE_PURCHASE -> Known.REAL_ESTATE_PURCHASE + LOAN_PAYMENT -> Known.LOAN_PAYMENT + TAX_PAYMENT -> Known.TAX_PAYMENT + UTILITY_BILL -> Known.UTILITY_BILL + DONATION -> Known.DONATION + TRAVEL -> Known.TRAVEL + OTHER -> Known.OTHER + else -> throw GridInvalidDataException("Unknown PurposeOfPayment: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): PurposeOfPayment = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PurposeOfPayment && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is NgnAccount && + accountNumber == other.accountNumber && + accountType == other.accountType && + bankName == other.bankName && + beneficiary == other.beneficiary && + purposeOfPayment == other.purposeOfPayment && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountNumber, + accountType, + bankName, + beneficiary, + purposeOfPayment, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "NgnAccount{accountNumber=$accountNumber, accountType=$accountType, bankName=$bankName, beneficiary=$beneficiary, purposeOfPayment=$purposeOfPayment, additionalProperties=$additionalProperties}" + } + + class CadAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountNumber: JsonField, + private val accountType: JsonValue, + private val bankCode: JsonField, + private val beneficiary: JsonField, + private val branchCode: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("bankCode") + @ExcludeMissing + bankCode: JsonField = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("branchCode") + @ExcludeMissing + branchCode: JsonField = JsonMissing.of(), + ) : this(accountNumber, accountType, bankCode, beneficiary, branchCode, mutableMapOf()) + + /** + * Bank account number (7-12 digits) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("CAD_ACCOUNT") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Canadian financial institution number (3 digits) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun bankCode(): String = bankCode.getRequired("bankCode") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * Transit number identifying the branch (5 digits) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun branchCode(): String = branchCode.getRequired("branchCode") + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [bankCode]. + * + * Unlike [bankCode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bankCode") @ExcludeMissing fun _bankCode(): JsonField = bankCode + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [branchCode]. + * + * Unlike [branchCode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("branchCode") + @ExcludeMissing + fun _branchCode(): JsonField = branchCode + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CadAccount]. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankCode() + * .beneficiary() + * .branchCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CadAccount]. */ + class Builder internal constructor() { + + private var accountNumber: JsonField? = null + private var accountType: JsonValue = JsonValue.from("CAD_ACCOUNT") + private var bankCode: JsonField? = null + private var beneficiary: JsonField? = null + private var branchCode: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(cadAccount: CadAccount) = apply { + accountNumber = cadAccount.accountNumber + accountType = cadAccount.accountType + bankCode = cadAccount.bankCode + beneficiary = cadAccount.beneficiary + branchCode = cadAccount.branchCode + additionalProperties = cadAccount.additionalProperties.toMutableMap() + } + + /** Bank account number (7-12 digits) */ + fun accountNumber(accountNumber: String) = accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("CAD_ACCOUNT") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Canadian financial institution number (3 digits) */ + fun bankCode(bankCode: String) = bankCode(JsonField.of(bankCode)) + + /** + * Sets [Builder.bankCode] to an arbitrary JSON value. + * + * You should usually call [Builder.bankCode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bankCode(bankCode: JsonField) = apply { this.bankCode = bankCode } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** Transit number identifying the branch (5 digits) */ + fun branchCode(branchCode: String) = branchCode(JsonField.of(branchCode)) + + /** + * Sets [Builder.branchCode] to an arbitrary JSON value. + * + * You should usually call [Builder.branchCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun branchCode(branchCode: JsonField) = apply { this.branchCode = branchCode } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CadAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankCode() + * .beneficiary() + * .branchCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CadAccount = + CadAccount( + checkRequired("accountNumber", accountNumber), + accountType, + checkRequired("bankCode", bankCode), + checkRequired("beneficiary", beneficiary), + checkRequired("branchCode", branchCode), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CadAccount = apply { + if (validated) { + return@apply + } + + accountNumber() + _accountType().let { + if (it != JsonValue.from("CAD_ACCOUNT")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + bankCode() + beneficiary().validate() + branchCode() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountNumber.asKnown() == null) 0 else 1) + + accountType.let { if (it == JsonValue.from("CAD_ACCOUNT")) 1 else 0 } + + (if (bankCode.asKnown() == null) 0 else 1) + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (branchCode.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CadAccount && + accountNumber == other.accountNumber && + accountType == other.accountType && + bankCode == other.bankCode && + beneficiary == other.beneficiary && + branchCode == other.branchCode && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountNumber, + accountType, + bankCode, + beneficiary, + branchCode, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CadAccount{accountNumber=$accountNumber, accountType=$accountType, bankCode=$bankCode, beneficiary=$beneficiary, branchCode=$branchCode, additionalProperties=$additionalProperties}" + } + + class GbpAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountNumber: JsonField, + private val accountType: JsonValue, + private val beneficiary: JsonField, + private val sortCode: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("sortCode") @ExcludeMissing sortCode: JsonField = JsonMissing.of(), + ) : this(accountNumber, accountType, beneficiary, sortCode, mutableMapOf()) + + /** + * UK bank account number (8 digits) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("GBP_ACCOUNT") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * UK bank sort code (6 digits, may include hyphens) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun sortCode(): String = sortCode.getRequired("sortCode") + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [sortCode]. + * + * Unlike [sortCode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sortCode") @ExcludeMissing fun _sortCode(): JsonField = sortCode + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [GbpAccount]. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .beneficiary() + * .sortCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [GbpAccount]. */ + class Builder internal constructor() { + + private var accountNumber: JsonField? = null + private var accountType: JsonValue = JsonValue.from("GBP_ACCOUNT") + private var beneficiary: JsonField? = null + private var sortCode: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(gbpAccount: GbpAccount) = apply { + accountNumber = gbpAccount.accountNumber + accountType = gbpAccount.accountType + beneficiary = gbpAccount.beneficiary + sortCode = gbpAccount.sortCode + additionalProperties = gbpAccount.additionalProperties.toMutableMap() + } + + /** UK bank account number (8 digits) */ + fun accountNumber(accountNumber: String) = accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("GBP_ACCOUNT") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** UK bank sort code (6 digits, may include hyphens) */ + fun sortCode(sortCode: String) = sortCode(JsonField.of(sortCode)) + + /** + * Sets [Builder.sortCode] to an arbitrary JSON value. + * + * You should usually call [Builder.sortCode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sortCode(sortCode: JsonField) = apply { this.sortCode = sortCode } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [GbpAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .beneficiary() + * .sortCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): GbpAccount = + GbpAccount( + checkRequired("accountNumber", accountNumber), + accountType, + checkRequired("beneficiary", beneficiary), + checkRequired("sortCode", sortCode), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): GbpAccount = apply { + if (validated) { + return@apply + } + + accountNumber() + _accountType().let { + if (it != JsonValue.from("GBP_ACCOUNT")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + beneficiary().validate() + sortCode() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountNumber.asKnown() == null) 0 else 1) + + accountType.let { if (it == JsonValue.from("GBP_ACCOUNT")) 1 else 0 } + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (sortCode.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GbpAccount && + accountNumber == other.accountNumber && + accountType == other.accountType && + beneficiary == other.beneficiary && + sortCode == other.sortCode && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountNumber, accountType, beneficiary, sortCode, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GbpAccount{accountNumber=$accountNumber, accountType=$accountType, beneficiary=$beneficiary, sortCode=$sortCode, additionalProperties=$additionalProperties}" + } + + class PhpAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountNumber: JsonField, + private val accountType: JsonValue, + private val bankName: JsonField, + private val beneficiary: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("bankName") + @ExcludeMissing + bankName: JsonField = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + ) : this(accountNumber, accountType, bankName, beneficiary, mutableMapOf()) + + /** + * Bank account number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("PHP_ACCOUNT") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Name of the beneficiary's bank + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun bankName(): String = bankName.getRequired("bankName") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [bankName]. + * + * Unlike [bankName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bankName") @ExcludeMissing fun _bankName(): JsonField = bankName + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PhpAccount]. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankName() + * .beneficiary() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PhpAccount]. */ + class Builder internal constructor() { + + private var accountNumber: JsonField? = null + private var accountType: JsonValue = JsonValue.from("PHP_ACCOUNT") + private var bankName: JsonField? = null + private var beneficiary: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(phpAccount: PhpAccount) = apply { + accountNumber = phpAccount.accountNumber + accountType = phpAccount.accountType + bankName = phpAccount.bankName + beneficiary = phpAccount.beneficiary + additionalProperties = phpAccount.additionalProperties.toMutableMap() + } + + /** Bank account number */ + fun accountNumber(accountNumber: String) = accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("PHP_ACCOUNT") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Name of the beneficiary's bank */ + fun bankName(bankName: String) = bankName(JsonField.of(bankName)) + + /** + * Sets [Builder.bankName] to an arbitrary JSON value. + * + * You should usually call [Builder.bankName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bankName(bankName: JsonField) = apply { this.bankName = bankName } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PhpAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankName() + * .beneficiary() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PhpAccount = + PhpAccount( + checkRequired("accountNumber", accountNumber), + accountType, + checkRequired("bankName", bankName), + checkRequired("beneficiary", beneficiary), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PhpAccount = apply { + if (validated) { + return@apply + } + + accountNumber() + _accountType().let { + if (it != JsonValue.from("PHP_ACCOUNT")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + bankName() + beneficiary().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountNumber.asKnown() == null) 0 else 1) + + accountType.let { if (it == JsonValue.from("PHP_ACCOUNT")) 1 else 0 } + + (if (bankName.asKnown() == null) 0 else 1) + + (beneficiary.asKnown()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PhpAccount && + accountNumber == other.accountNumber && + accountType == other.accountType && + bankName == other.bankName && + beneficiary == other.beneficiary && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountNumber, accountType, bankName, beneficiary, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PhpAccount{accountNumber=$accountNumber, accountType=$accountType, bankName=$bankName, beneficiary=$beneficiary, additionalProperties=$additionalProperties}" + } + + class SgdAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountNumber: JsonField, + private val accountType: JsonValue, + private val bankName: JsonField, + private val beneficiary: JsonField, + private val swiftCode: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("bankName") + @ExcludeMissing + bankName: JsonField = JsonMissing.of(), + @JsonProperty("beneficiary") + @ExcludeMissing + beneficiary: JsonField = JsonMissing.of(), + @JsonProperty("swiftCode") + @ExcludeMissing + swiftCode: JsonField = JsonMissing.of(), + ) : this(accountNumber, accountType, bankName, beneficiary, swiftCode, mutableMapOf()) + + /** + * Bank account number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("SGD_ACCOUNT") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Name of the beneficiary's bank + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun bankName(): String = bankName.getRequired("bankName") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun beneficiary(): BeneficiaryOneOf = beneficiary.getRequired("beneficiary") + + /** + * SWIFT/BIC code (8 or 11 characters) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun swiftCode(): String = swiftCode.getRequired("swiftCode") + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [bankName]. + * + * Unlike [bankName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bankName") @ExcludeMissing fun _bankName(): JsonField = bankName + + /** + * Returns the raw JSON value of [beneficiary]. + * + * Unlike [beneficiary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("beneficiary") + @ExcludeMissing + fun _beneficiary(): JsonField = beneficiary + + /** + * Returns the raw JSON value of [swiftCode]. + * + * Unlike [swiftCode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("swiftCode") @ExcludeMissing fun _swiftCode(): JsonField = swiftCode + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SgdAccount]. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankName() + * .beneficiary() + * .swiftCode() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SgdAccount]. */ + class Builder internal constructor() { + + private var accountNumber: JsonField? = null + private var accountType: JsonValue = JsonValue.from("SGD_ACCOUNT") + private var bankName: JsonField? = null + private var beneficiary: JsonField? = null + private var swiftCode: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(sgdAccount: SgdAccount) = apply { + accountNumber = sgdAccount.accountNumber + accountType = sgdAccount.accountType + bankName = sgdAccount.bankName + beneficiary = sgdAccount.beneficiary + swiftCode = sgdAccount.swiftCode + additionalProperties = sgdAccount.additionalProperties.toMutableMap() + } + + /** Bank account number */ + fun accountNumber(accountNumber: String) = accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("SGD_ACCOUNT") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Name of the beneficiary's bank */ + fun bankName(bankName: String) = bankName(JsonField.of(bankName)) + + /** + * Sets [Builder.bankName] to an arbitrary JSON value. + * + * You should usually call [Builder.bankName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bankName(bankName: JsonField) = apply { this.bankName = bankName } + + fun beneficiary(beneficiary: BeneficiaryOneOf) = beneficiary(JsonField.of(beneficiary)) + + /** + * Sets [Builder.beneficiary] to an arbitrary JSON value. + * + * You should usually call [Builder.beneficiary] with a well-typed [BeneficiaryOneOf] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun beneficiary(beneficiary: JsonField) = apply { + this.beneficiary = beneficiary + } + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofIndividual(individual)`. */ + fun beneficiary(individual: BeneficiaryOneOf.Individual) = + beneficiary(BeneficiaryOneOf.ofIndividual(individual)) + + /** Alias for calling [beneficiary] with `BeneficiaryOneOf.ofBusiness(business)`. */ + fun beneficiary(business: BeneficiaryOneOf.Business) = + beneficiary(BeneficiaryOneOf.ofBusiness(business)) + + /** + * Alias for calling [beneficiary] with the following: + * ```kotlin + * BeneficiaryOneOf.Business.builder() + * .legalName(legalName) + * .build() + * ``` + */ + fun businessBeneficiary(legalName: String) = + beneficiary(BeneficiaryOneOf.Business.builder().legalName(legalName).build()) + + /** SWIFT/BIC code (8 or 11 characters) */ + fun swiftCode(swiftCode: String) = swiftCode(JsonField.of(swiftCode)) + + /** + * Sets [Builder.swiftCode] to an arbitrary JSON value. + * + * You should usually call [Builder.swiftCode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun swiftCode(swiftCode: JsonField) = apply { this.swiftCode = swiftCode } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SgdAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountNumber() + * .bankName() + * .beneficiary() + * .swiftCode() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SgdAccount = + SgdAccount( + checkRequired("accountNumber", accountNumber), + accountType, + checkRequired("bankName", bankName), + checkRequired("beneficiary", beneficiary), + checkRequired("swiftCode", swiftCode), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SgdAccount = apply { + if (validated) { + return@apply + } + + accountNumber() + _accountType().let { + if (it != JsonValue.from("SGD_ACCOUNT")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + bankName() + beneficiary().validate() + swiftCode() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountNumber.asKnown() == null) 0 else 1) + + accountType.let { if (it == JsonValue.from("SGD_ACCOUNT")) 1 else 0 } + + (if (bankName.asKnown() == null) 0 else 1) + + (beneficiary.asKnown()?.validity() ?: 0) + + (if (swiftCode.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SgdAccount && + accountNumber == other.accountNumber && + accountType == other.accountType && + bankName == other.bankName && + beneficiary == other.beneficiary && + swiftCode == other.swiftCode && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountNumber, + accountType, + bankName, + beneficiary, + swiftCode, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SgdAccount{accountNumber=$accountNumber, accountType=$accountType, bankName=$bankName, beneficiary=$beneficiary, swiftCode=$swiftCode, additionalProperties=$additionalProperties}" + } + + class SparkWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val address: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField = JsonMissing.of(), + ) : this(accountType, address, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("SPARK_WALLET") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Spark wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun address(): String = address.getRequired("address") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SparkWallet]. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SparkWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("SPARK_WALLET") + private var address: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(sparkWallet: SparkWallet) = apply { + accountType = sparkWallet.accountType + address = sparkWallet.address + additionalProperties = sparkWallet.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("SPARK_WALLET") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Spark wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SparkWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SparkWallet = + SparkWallet( + accountType, + checkRequired("address", address), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SparkWallet = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("SPARK_WALLET")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + address() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("SPARK_WALLET")) 1 else 0 } + + (if (address.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SparkWallet && + accountType == other.accountType && + address == other.address && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SparkWallet{accountType=$accountType, address=$address, additionalProperties=$additionalProperties}" + } + + class Lightning + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val bolt12: JsonField, + private val invoice: JsonField, + private val lightningAddress: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("bolt12") @ExcludeMissing bolt12: JsonField = JsonMissing.of(), + @JsonProperty("invoice") @ExcludeMissing invoice: JsonField = JsonMissing.of(), + @JsonProperty("lightningAddress") + @ExcludeMissing + lightningAddress: JsonField = JsonMissing.of(), + ) : this(accountType, bolt12, invoice, lightningAddress, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("LIGHTNING") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * A bolt12 offer which can be reused as a payment destination + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun bolt12(): String? = bolt12.getNullable("bolt12") + + /** + * 1-time use lightning bolt11 invoice payout destination + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun invoice(): String? = invoice.getNullable("invoice") + + /** + * A lightning address which can be used as a payment destination. Note that for UMA + * addresses, no external account is needed. You can use the UMA address directly as a + * destination. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lightningAddress(): String? = lightningAddress.getNullable("lightningAddress") + + /** + * Returns the raw JSON value of [bolt12]. + * + * Unlike [bolt12], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bolt12") @ExcludeMissing fun _bolt12(): JsonField = bolt12 + + /** + * Returns the raw JSON value of [invoice]. + * + * Unlike [invoice], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("invoice") @ExcludeMissing fun _invoice(): JsonField = invoice + + /** + * Returns the raw JSON value of [lightningAddress]. + * + * Unlike [lightningAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("lightningAddress") + @ExcludeMissing + fun _lightningAddress(): JsonField = lightningAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Lightning]. */ + fun builder() = Builder() + } + + /** A builder for [Lightning]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("LIGHTNING") + private var bolt12: JsonField = JsonMissing.of() + private var invoice: JsonField = JsonMissing.of() + private var lightningAddress: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(lightning: Lightning) = apply { + accountType = lightning.accountType + bolt12 = lightning.bolt12 + invoice = lightning.invoice + lightningAddress = lightning.lightningAddress + additionalProperties = lightning.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("LIGHTNING") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** A bolt12 offer which can be reused as a payment destination */ + fun bolt12(bolt12: String) = bolt12(JsonField.of(bolt12)) + + /** + * Sets [Builder.bolt12] to an arbitrary JSON value. + * + * You should usually call [Builder.bolt12] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bolt12(bolt12: JsonField) = apply { this.bolt12 = bolt12 } + + /** 1-time use lightning bolt11 invoice payout destination */ + fun invoice(invoice: String) = invoice(JsonField.of(invoice)) + + /** + * Sets [Builder.invoice] to an arbitrary JSON value. + * + * You should usually call [Builder.invoice] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun invoice(invoice: JsonField) = apply { this.invoice = invoice } + + /** + * A lightning address which can be used as a payment destination. Note that for UMA + * addresses, no external account is needed. You can use the UMA address directly as a + * destination. + */ + fun lightningAddress(lightningAddress: String) = + lightningAddress(JsonField.of(lightningAddress)) + + /** + * Sets [Builder.lightningAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.lightningAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lightningAddress(lightningAddress: JsonField) = apply { + this.lightningAddress = lightningAddress + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Lightning]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Lightning = + Lightning( + accountType, + bolt12, + invoice, + lightningAddress, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Lightning = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("LIGHTNING")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + bolt12() + invoice() + lightningAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("LIGHTNING")) 1 else 0 } + + (if (bolt12.asKnown() == null) 0 else 1) + + (if (invoice.asKnown() == null) 0 else 1) + + (if (lightningAddress.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Lightning && + accountType == other.accountType && + bolt12 == other.bolt12 && + invoice == other.invoice && + lightningAddress == other.lightningAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, bolt12, invoice, lightningAddress, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Lightning{accountType=$accountType, bolt12=$bolt12, invoice=$invoice, lightningAddress=$lightningAddress, additionalProperties=$additionalProperties}" + } + + class SolanaWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val address: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField = JsonMissing.of(), + ) : this(accountType, address, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("SOLANA_WALLET") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Solana wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun address(): String = address.getRequired("address") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SolanaWallet]. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SolanaWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("SOLANA_WALLET") + private var address: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(solanaWallet: SolanaWallet) = apply { + accountType = solanaWallet.accountType + address = solanaWallet.address + additionalProperties = solanaWallet.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("SOLANA_WALLET") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Solana wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SolanaWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SolanaWallet = + SolanaWallet( + accountType, + checkRequired("address", address), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SolanaWallet = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("SOLANA_WALLET")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + address() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("SOLANA_WALLET")) 1 else 0 } + + (if (address.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SolanaWallet && + accountType == other.accountType && + address == other.address && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SolanaWallet{accountType=$accountType, address=$address, additionalProperties=$additionalProperties}" + } + + class TronWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val address: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField = JsonMissing.of(), + ) : this(accountType, address, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("TRON_WALLET") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Tron wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun address(): String = address.getRequired("address") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TronWallet]. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TronWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("TRON_WALLET") + private var address: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(tronWallet: TronWallet) = apply { + accountType = tronWallet.accountType + address = tronWallet.address + additionalProperties = tronWallet.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("TRON_WALLET") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Tron wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [TronWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TronWallet = + TronWallet( + accountType, + checkRequired("address", address), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): TronWallet = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("TRON_WALLET")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + address() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("TRON_WALLET")) 1 else 0 } + + (if (address.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TronWallet && + accountType == other.accountType && + address == other.address && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TronWallet{accountType=$accountType, address=$address, additionalProperties=$additionalProperties}" + } + + class PolygonWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val address: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField = JsonMissing.of(), + ) : this(accountType, address, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("POLYGON_WALLET") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Polygon eth wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun address(): String = address.getRequired("address") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PolygonWallet]. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PolygonWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("POLYGON_WALLET") + private var address: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(polygonWallet: PolygonWallet) = apply { + accountType = polygonWallet.accountType + address = polygonWallet.address + additionalProperties = polygonWallet.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("POLYGON_WALLET") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Polygon eth wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PolygonWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PolygonWallet = + PolygonWallet( + accountType, + checkRequired("address", address), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PolygonWallet = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("POLYGON_WALLET")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + address() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("POLYGON_WALLET")) 1 else 0 } + + (if (address.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PolygonWallet && + accountType == other.accountType && + address == other.address && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PolygonWallet{accountType=$accountType, address=$address, additionalProperties=$additionalProperties}" + } + + class BaseWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonValue, + private val address: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") @ExcludeMissing accountType: JsonValue = JsonMissing.of(), + @JsonProperty("address") @ExcludeMissing address: JsonField = JsonMissing.of(), + ) : this(accountType, address, mutableMapOf()) + + /** + * Expected to always return the following: + * ```kotlin + * JsonValue.from("BASE_WALLET") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server + * responded with an unexpected value). + */ + @JsonProperty("accountType") @ExcludeMissing fun _accountType(): JsonValue = accountType + + /** + * Base eth wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun address(): String = address.getRequired("address") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BaseWallet]. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BaseWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonValue = JsonValue.from("BASE_WALLET") + private var address: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(baseWallet: BaseWallet) = apply { + accountType = baseWallet.accountType + address = baseWallet.address + additionalProperties = baseWallet.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```kotlin + * JsonValue.from("BASE_WALLET") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountType(accountType: JsonValue) = apply { this.accountType = accountType } + + /** Base eth wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BaseWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BaseWallet = + BaseWallet( + accountType, + checkRequired("address", address), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BaseWallet = apply { + if (validated) { + return@apply + } + + _accountType().let { + if (it != JsonValue.from("BASE_WALLET")) { + throw GridInvalidDataException("'accountType' is invalid, received $it") + } + } + address() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accountType.let { if (it == JsonValue.from("BASE_WALLET")) 1 else 0 } + + (if (address.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BaseWallet && + accountType == other.accountType && + address == other.address && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BaseWallet{accountType=$accountType, address=$address, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPage.kt new file mode 100644 index 00000000..09ad78e3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPage.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.services.blocking.customers.ExternalAccountService +import java.util.Objects + +/** @see ExternalAccountService.list */ +class ExternalAccountListPage +private constructor( + private val service: ExternalAccountService, + private val params: ExternalAccountListParams, + private val response: ExternalAccountListPageResponse, +) : Page { + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): ExternalAccountListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): ExternalAccountListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): ExternalAccountListParams = params + + /** The response that this page was parsed from. */ + fun response(): ExternalAccountListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountListPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountListPage]. */ + class Builder internal constructor() { + + private var service: ExternalAccountService? = null + private var params: ExternalAccountListParams? = null + private var response: ExternalAccountListPageResponse? = null + + internal fun from(externalAccountListPage: ExternalAccountListPage) = apply { + service = externalAccountListPage.service + params = externalAccountListPage.params + response = externalAccountListPage.response + } + + fun service(service: ExternalAccountService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: ExternalAccountListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: ExternalAccountListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [ExternalAccountListPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountListPage = + ExternalAccountListPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountListPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "ExternalAccountListPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageAsync.kt new file mode 100644 index 00000000..3c26be61 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageAsync.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.services.async.customers.ExternalAccountServiceAsync +import java.util.Objects + +/** @see ExternalAccountServiceAsync.list */ +class ExternalAccountListPageAsync +private constructor( + private val service: ExternalAccountServiceAsync, + private val params: ExternalAccountListParams, + private val response: ExternalAccountListPageResponse, +) : PageAsync { + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [ExternalAccountListPageResponse], but gracefully handles missing data. + * + * @see ExternalAccountListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): ExternalAccountListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): ExternalAccountListPageAsync = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): ExternalAccountListParams = params + + /** The response that this page was parsed from. */ + fun response(): ExternalAccountListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountListPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountListPageAsync]. */ + class Builder internal constructor() { + + private var service: ExternalAccountServiceAsync? = null + private var params: ExternalAccountListParams? = null + private var response: ExternalAccountListPageResponse? = null + + internal fun from(externalAccountListPageAsync: ExternalAccountListPageAsync) = apply { + service = externalAccountListPageAsync.service + params = externalAccountListPageAsync.params + response = externalAccountListPageAsync.response + } + + fun service(service: ExternalAccountServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: ExternalAccountListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: ExternalAccountListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [ExternalAccountListPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountListPageAsync = + ExternalAccountListPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountListPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "ExternalAccountListPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponse.kt new file mode 100644 index 00000000..4f4ff57f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponse.kt @@ -0,0 +1,304 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class ExternalAccountListPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") + @ExcludeMissing + data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of external accounts matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun hasMore(): Boolean = hasMore.getRequired("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of external accounts matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [ExternalAccountListPageResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountListPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField? = null + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(externalAccountListPageResponse: ExternalAccountListPageResponse) = + apply { + data = externalAccountListPageResponse.data.map { it.toMutableList() } + hasMore = externalAccountListPageResponse.hasMore + nextCursor = externalAccountListPageResponse.nextCursor + totalCount = externalAccountListPageResponse.totalCount + additionalProperties = + externalAccountListPageResponse.additionalProperties.toMutableMap() + } + + /** List of external accounts matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [ExternalAccount] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: ExternalAccount) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of external accounts matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ExternalAccountListPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountListPageResponse = + ExternalAccountListPageResponse( + checkRequired("data", data).map { it.toImmutable() }, + checkRequired("hasMore", hasMore), + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ExternalAccountListPageResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountListPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ExternalAccountListPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParams.kt new file mode 100644 index 00000000..e0e76e50 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParams.kt @@ -0,0 +1,242 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Retrieve a list of external accounts with optional filtering parameters. Returns all external + * accounts that match the specified filters. If no filters are provided, returns all external + * accounts (paginated). + * + * External accounts are bank accounts, cryptocurrency wallets, or other payment destinations that + * customers can use to receive funds from the platform. + */ +class ExternalAccountListParams +private constructor( + private val currency: String?, + private val cursor: String?, + private val customerId: String?, + private val limit: Long?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter by currency code */ + fun currency(): String? = currency + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** Filter by external accounts associated with a specific customer */ + fun customerId(): String? = customerId + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ExternalAccountListParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountListParams]. + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountListParams]. */ + class Builder internal constructor() { + + private var currency: String? = null + private var cursor: String? = null + private var customerId: String? = null + private var limit: Long? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(externalAccountListParams: ExternalAccountListParams) = apply { + currency = externalAccountListParams.currency + cursor = externalAccountListParams.cursor + customerId = externalAccountListParams.customerId + limit = externalAccountListParams.limit + additionalHeaders = externalAccountListParams.additionalHeaders.toBuilder() + additionalQueryParams = externalAccountListParams.additionalQueryParams.toBuilder() + } + + /** Filter by currency code */ + fun currency(currency: String?) = apply { this.currency = currency } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** Filter by external accounts associated with a specific customer */ + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExternalAccountListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ExternalAccountListParams = + ExternalAccountListParams( + currency, + cursor, + customerId, + limit, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + currency?.let { put("currency", it) } + cursor?.let { put("cursor", it) } + customerId?.let { put("customerId", it) } + limit?.let { put("limit", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountListParams && + currency == other.currency && + cursor == other.cursor && + customerId == other.customerId && + limit == other.limit && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(currency, cursor, customerId, limit, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ExternalAccountListParams{currency=$currency, cursor=$cursor, customerId=$customerId, limit=$limit, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParams.kt new file mode 100644 index 00000000..3ba3cbbb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParams.kt @@ -0,0 +1,260 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.exchangerates + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import java.util.Objects + +/** + * Retrieve cached exchange rates for currency corridors. Returns FX rates that are cached for + * approximately 5 minutes. Rates include fees specific to your platform for authenticated requests. + * + * **Filtering Options:** + * - Filter by source currency to get all available destination corridors + * - Filter by specific destination currency or currencies + * - Provide a sending amount to get calculated receiving amounts + */ +class ExchangeRateListParams +private constructor( + private val destinationCurrency: List?, + private val sendingAmount: Long?, + private val sourceCurrency: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Filter by destination currency code(s). Can be repeated for multiple currencies (e.g., + * &destinationCurrency=INR&destinationCurrency=GBP) + */ + fun destinationCurrency(): List? = destinationCurrency + + /** + * Sending amount in the smallest unit of the source currency (e.g., cents for USD). If no + * amount is provided, the default is 10000 in the sending currency smallest unit. + */ + fun sendingAmount(): Long? = sendingAmount + + /** Filter by source currency code (e.g., USD) */ + fun sourceCurrency(): String? = sourceCurrency + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ExchangeRateListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ExchangeRateListParams]. */ + fun builder() = Builder() + } + + /** A builder for [ExchangeRateListParams]. */ + class Builder internal constructor() { + + private var destinationCurrency: MutableList? = null + private var sendingAmount: Long? = null + private var sourceCurrency: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(exchangeRateListParams: ExchangeRateListParams) = apply { + destinationCurrency = exchangeRateListParams.destinationCurrency?.toMutableList() + sendingAmount = exchangeRateListParams.sendingAmount + sourceCurrency = exchangeRateListParams.sourceCurrency + additionalHeaders = exchangeRateListParams.additionalHeaders.toBuilder() + additionalQueryParams = exchangeRateListParams.additionalQueryParams.toBuilder() + } + + /** + * Filter by destination currency code(s). Can be repeated for multiple currencies (e.g., + * &destinationCurrency=INR&destinationCurrency=GBP) + */ + fun destinationCurrency(destinationCurrency: List?) = apply { + this.destinationCurrency = destinationCurrency?.toMutableList() + } + + /** + * Adds a single [String] to [Builder.destinationCurrency]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addDestinationCurrency(destinationCurrency: String) = apply { + this.destinationCurrency = + (this.destinationCurrency ?: mutableListOf()).apply { add(destinationCurrency) } + } + + /** + * Sending amount in the smallest unit of the source currency (e.g., cents for USD). If no + * amount is provided, the default is 10000 in the sending currency smallest unit. + */ + fun sendingAmount(sendingAmount: Long?) = apply { this.sendingAmount = sendingAmount } + + /** + * Alias for [Builder.sendingAmount]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun sendingAmount(sendingAmount: Long) = sendingAmount(sendingAmount as Long?) + + /** Filter by source currency code (e.g., USD) */ + fun sourceCurrency(sourceCurrency: String?) = apply { this.sourceCurrency = sourceCurrency } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExchangeRateListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ExchangeRateListParams = + ExchangeRateListParams( + destinationCurrency?.toImmutable(), + sendingAmount, + sourceCurrency, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + destinationCurrency?.let { put("destinationCurrency", it.joinToString(",")) } + sendingAmount?.let { put("sendingAmount", it.toString()) } + sourceCurrency?.let { put("sourceCurrency", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExchangeRateListParams && + destinationCurrency == other.destinationCurrency && + sendingAmount == other.sendingAmount && + sourceCurrency == other.sourceCurrency && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + destinationCurrency, + sendingAmount, + sourceCurrency, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "ExchangeRateListParams{destinationCurrency=$destinationCurrency, sendingAmount=$sendingAmount, sourceCurrency=$sourceCurrency, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponse.kt new file mode 100644 index 00000000..95cbc1f3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponse.kt @@ -0,0 +1,873 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.exchangerates + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.quotes.Currency +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class ExchangeRateListResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField> = JsonMissing.of() + ) : this(data, mutableMapOf()) + + /** + * List of exchange rates matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExchangeRateListResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExchangeRateListResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(exchangeRateListResponse: ExchangeRateListResponse) = apply { + data = exchangeRateListResponse.data.map { it.toMutableList() } + additionalProperties = exchangeRateListResponse.additionalProperties.toMutableMap() + } + + /** List of exchange rates matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [Data] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: Data) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ExchangeRateListResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExchangeRateListResponse = + ExchangeRateListResponse( + checkRequired("data", data).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ExchangeRateListResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + /** Exchange rate information for a currency corridor */ + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val destinationCurrency: JsonField, + private val destinationPaymentRail: JsonField, + private val exchangeRate: JsonField, + private val fees: JsonField, + private val receivingAmount: JsonField, + private val sourceCurrency: JsonField, + private val updatedAt: JsonField, + private val sendingAmount: JsonField, + private val sourcePaymentRail: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("destinationCurrency") + @ExcludeMissing + destinationCurrency: JsonField = JsonMissing.of(), + @JsonProperty("destinationPaymentRail") + @ExcludeMissing + destinationPaymentRail: JsonField = JsonMissing.of(), + @JsonProperty("exchangeRate") + @ExcludeMissing + exchangeRate: JsonField = JsonMissing.of(), + @JsonProperty("fees") @ExcludeMissing fees: JsonField = JsonMissing.of(), + @JsonProperty("receivingAmount") + @ExcludeMissing + receivingAmount: JsonField = JsonMissing.of(), + @JsonProperty("sourceCurrency") + @ExcludeMissing + sourceCurrency: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("sendingAmount") + @ExcludeMissing + sendingAmount: JsonField = JsonMissing.of(), + @JsonProperty("sourcePaymentRail") + @ExcludeMissing + sourcePaymentRail: JsonField = JsonMissing.of(), + ) : this( + destinationCurrency, + destinationPaymentRail, + exchangeRate, + fees, + receivingAmount, + sourceCurrency, + updatedAt, + sendingAmount, + sourcePaymentRail, + mutableMapOf(), + ) + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destinationCurrency(): Currency = destinationCurrency.getRequired("destinationCurrency") + + /** + * The payment rail used for the destination (e.g., UPI, SEPA_INSTANT, MOBILE_MONEY, + * FASTER_PAYMENTS) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destinationPaymentRail(): String = + destinationPaymentRail.getRequired("destinationPaymentRail") + + /** + * Number of destination currency units per sending currency unit + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun exchangeRate(): Double = exchangeRate.getRequired("exchangeRate") + + /** + * Fees associated with an exchange rate + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun fees(): Fees = fees.getRequired("fees") + + /** + * The receiving amount in the smallest unit of the destination currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivingAmount(): Long = receivingAmount.getRequired("receivingAmount") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun sourceCurrency(): Currency = sourceCurrency.getRequired("sourceCurrency") + + /** + * Timestamp when this exchange rate was last refreshed + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime = updatedAt.getRequired("updatedAt") + + /** + * The sending amount in the smallest unit of the source currency (e.g., cents for USD). + * Echoed back from the request if provided. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun sendingAmount(): Long? = sendingAmount.getNullable("sendingAmount") + + /** + * The payment rail used for the source (e.g., ACCOUNT, RTP) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun sourcePaymentRail(): String? = sourcePaymentRail.getNullable("sourcePaymentRail") + + /** + * Returns the raw JSON value of [destinationCurrency]. + * + * Unlike [destinationCurrency], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("destinationCurrency") + @ExcludeMissing + fun _destinationCurrency(): JsonField = destinationCurrency + + /** + * Returns the raw JSON value of [destinationPaymentRail]. + * + * Unlike [destinationPaymentRail], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("destinationPaymentRail") + @ExcludeMissing + fun _destinationPaymentRail(): JsonField = destinationPaymentRail + + /** + * Returns the raw JSON value of [exchangeRate]. + * + * Unlike [exchangeRate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("exchangeRate") + @ExcludeMissing + fun _exchangeRate(): JsonField = exchangeRate + + /** + * Returns the raw JSON value of [fees]. + * + * Unlike [fees], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fees") @ExcludeMissing fun _fees(): JsonField = fees + + /** + * Returns the raw JSON value of [receivingAmount]. + * + * Unlike [receivingAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("receivingAmount") + @ExcludeMissing + fun _receivingAmount(): JsonField = receivingAmount + + /** + * Returns the raw JSON value of [sourceCurrency]. + * + * Unlike [sourceCurrency], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("sourceCurrency") + @ExcludeMissing + fun _sourceCurrency(): JsonField = sourceCurrency + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [sendingAmount]. + * + * Unlike [sendingAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("sendingAmount") + @ExcludeMissing + fun _sendingAmount(): JsonField = sendingAmount + + /** + * Returns the raw JSON value of [sourcePaymentRail]. + * + * Unlike [sourcePaymentRail], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("sourcePaymentRail") + @ExcludeMissing + fun _sourcePaymentRail(): JsonField = sourcePaymentRail + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Data]. + * + * The following fields are required: + * ```kotlin + * .destinationCurrency() + * .destinationPaymentRail() + * .exchangeRate() + * .fees() + * .receivingAmount() + * .sourceCurrency() + * .updatedAt() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var destinationCurrency: JsonField? = null + private var destinationPaymentRail: JsonField? = null + private var exchangeRate: JsonField? = null + private var fees: JsonField? = null + private var receivingAmount: JsonField? = null + private var sourceCurrency: JsonField? = null + private var updatedAt: JsonField? = null + private var sendingAmount: JsonField = JsonMissing.of() + private var sourcePaymentRail: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(data: Data) = apply { + destinationCurrency = data.destinationCurrency + destinationPaymentRail = data.destinationPaymentRail + exchangeRate = data.exchangeRate + fees = data.fees + receivingAmount = data.receivingAmount + sourceCurrency = data.sourceCurrency + updatedAt = data.updatedAt + sendingAmount = data.sendingAmount + sourcePaymentRail = data.sourcePaymentRail + additionalProperties = data.additionalProperties.toMutableMap() + } + + fun destinationCurrency(destinationCurrency: Currency) = + destinationCurrency(JsonField.of(destinationCurrency)) + + /** + * Sets [Builder.destinationCurrency] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationCurrency] with a well-typed [Currency] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun destinationCurrency(destinationCurrency: JsonField) = apply { + this.destinationCurrency = destinationCurrency + } + + /** + * The payment rail used for the destination (e.g., UPI, SEPA_INSTANT, MOBILE_MONEY, + * FASTER_PAYMENTS) + */ + fun destinationPaymentRail(destinationPaymentRail: String) = + destinationPaymentRail(JsonField.of(destinationPaymentRail)) + + /** + * Sets [Builder.destinationPaymentRail] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationPaymentRail] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun destinationPaymentRail(destinationPaymentRail: JsonField) = apply { + this.destinationPaymentRail = destinationPaymentRail + } + + /** Number of destination currency units per sending currency unit */ + fun exchangeRate(exchangeRate: Double) = exchangeRate(JsonField.of(exchangeRate)) + + /** + * Sets [Builder.exchangeRate] to an arbitrary JSON value. + * + * You should usually call [Builder.exchangeRate] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun exchangeRate(exchangeRate: JsonField) = apply { + this.exchangeRate = exchangeRate + } + + /** Fees associated with an exchange rate */ + fun fees(fees: Fees) = fees(JsonField.of(fees)) + + /** + * Sets [Builder.fees] to an arbitrary JSON value. + * + * You should usually call [Builder.fees] with a well-typed [Fees] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun fees(fees: JsonField) = apply { this.fees = fees } + + /** The receiving amount in the smallest unit of the destination currency */ + fun receivingAmount(receivingAmount: Long) = + receivingAmount(JsonField.of(receivingAmount)) + + /** + * Sets [Builder.receivingAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.receivingAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receivingAmount(receivingAmount: JsonField) = apply { + this.receivingAmount = receivingAmount + } + + fun sourceCurrency(sourceCurrency: Currency) = + sourceCurrency(JsonField.of(sourceCurrency)) + + /** + * Sets [Builder.sourceCurrency] to an arbitrary JSON value. + * + * You should usually call [Builder.sourceCurrency] with a well-typed [Currency] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sourceCurrency(sourceCurrency: JsonField) = apply { + this.sourceCurrency = sourceCurrency + } + + /** Timestamp when this exchange rate was last refreshed */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + /** + * The sending amount in the smallest unit of the source currency (e.g., cents for USD). + * Echoed back from the request if provided. + */ + fun sendingAmount(sendingAmount: Long) = sendingAmount(JsonField.of(sendingAmount)) + + /** + * Sets [Builder.sendingAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.sendingAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sendingAmount(sendingAmount: JsonField) = apply { + this.sendingAmount = sendingAmount + } + + /** The payment rail used for the source (e.g., ACCOUNT, RTP) */ + fun sourcePaymentRail(sourcePaymentRail: String) = + sourcePaymentRail(JsonField.of(sourcePaymentRail)) + + /** + * Sets [Builder.sourcePaymentRail] to an arbitrary JSON value. + * + * You should usually call [Builder.sourcePaymentRail] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sourcePaymentRail(sourcePaymentRail: JsonField) = apply { + this.sourcePaymentRail = sourcePaymentRail + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destinationCurrency() + * .destinationPaymentRail() + * .exchangeRate() + * .fees() + * .receivingAmount() + * .sourceCurrency() + * .updatedAt() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Data = + Data( + checkRequired("destinationCurrency", destinationCurrency), + checkRequired("destinationPaymentRail", destinationPaymentRail), + checkRequired("exchangeRate", exchangeRate), + checkRequired("fees", fees), + checkRequired("receivingAmount", receivingAmount), + checkRequired("sourceCurrency", sourceCurrency), + checkRequired("updatedAt", updatedAt), + sendingAmount, + sourcePaymentRail, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + destinationCurrency().validate() + destinationPaymentRail() + exchangeRate() + fees().validate() + receivingAmount() + sourceCurrency().validate() + updatedAt() + sendingAmount() + sourcePaymentRail() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (destinationCurrency.asKnown()?.validity() ?: 0) + + (if (destinationPaymentRail.asKnown() == null) 0 else 1) + + (if (exchangeRate.asKnown() == null) 0 else 1) + + (fees.asKnown()?.validity() ?: 0) + + (if (receivingAmount.asKnown() == null) 0 else 1) + + (sourceCurrency.asKnown()?.validity() ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (if (sendingAmount.asKnown() == null) 0 else 1) + + (if (sourcePaymentRail.asKnown() == null) 0 else 1) + + /** Fees associated with an exchange rate */ + class Fees + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val fixed: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("fixed") @ExcludeMissing fixed: JsonField = JsonMissing.of() + ) : this(fixed, mutableMapOf()) + + /** + * Fixed fee in the smallest unit of the sending currency (e.g., cents for USD) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun fixed(): Long? = fixed.getNullable("fixed") + + /** + * Returns the raw JSON value of [fixed]. + * + * Unlike [fixed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fixed") @ExcludeMissing fun _fixed(): JsonField = fixed + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Fees]. */ + fun builder() = Builder() + } + + /** A builder for [Fees]. */ + class Builder internal constructor() { + + private var fixed: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(fees: Fees) = apply { + fixed = fees.fixed + additionalProperties = fees.additionalProperties.toMutableMap() + } + + /** Fixed fee in the smallest unit of the sending currency (e.g., cents for USD) */ + fun fixed(fixed: Long) = fixed(JsonField.of(fixed)) + + /** + * Sets [Builder.fixed] to an arbitrary JSON value. + * + * You should usually call [Builder.fixed] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fixed(fixed: JsonField) = apply { this.fixed = fixed } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Fees]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Fees = Fees(fixed, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Fees = apply { + if (validated) { + return@apply + } + + fixed() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (fixed.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Fees && + fixed == other.fixed && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(fixed, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Fees{fixed=$fixed, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + destinationCurrency == other.destinationCurrency && + destinationPaymentRail == other.destinationPaymentRail && + exchangeRate == other.exchangeRate && + fees == other.fees && + receivingAmount == other.receivingAmount && + sourceCurrency == other.sourceCurrency && + updatedAt == other.updatedAt && + sendingAmount == other.sendingAmount && + sourcePaymentRail == other.sourcePaymentRail && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + destinationCurrency, + destinationPaymentRail, + exchangeRate, + fees, + receivingAmount, + sourceCurrency, + updatedAt, + sendingAmount, + sourcePaymentRail, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{destinationCurrency=$destinationCurrency, destinationPaymentRail=$destinationPaymentRail, exchangeRate=$exchangeRate, fees=$fees, receivingAmount=$receivingAmount, sourceCurrency=$sourceCurrency, updatedAt=$updatedAt, sendingAmount=$sendingAmount, sourcePaymentRail=$sourcePaymentRail, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExchangeRateListResponse && + data == other.data && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ExchangeRateListResponse{data=$data, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/CurrencyAmount.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/CurrencyAmount.kt new file mode 100644 index 00000000..8aa9fc26 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/CurrencyAmount.kt @@ -0,0 +1,209 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.quotes.Currency +import java.util.Collections +import java.util.Objects + +class CurrencyAmount +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val amount: JsonField, + private val currency: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("amount") @ExcludeMissing amount: JsonField = JsonMissing.of(), + @JsonProperty("currency") @ExcludeMissing currency: JsonField = JsonMissing.of(), + ) : this(amount, currency, mutableMapOf()) + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun amount(): Long = amount.getRequired("amount") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun currency(): Currency = currency.getRequired("currency") + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amount") @ExcludeMissing fun _amount(): JsonField = amount + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CurrencyAmount]. + * + * The following fields are required: + * ```kotlin + * .amount() + * .currency() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CurrencyAmount]. */ + class Builder internal constructor() { + + private var amount: JsonField? = null + private var currency: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(currencyAmount: CurrencyAmount) = apply { + amount = currencyAmount.amount + currency = currencyAmount.currency + additionalProperties = currencyAmount.additionalProperties.toMutableMap() + } + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + */ + fun amount(amount: Long) = amount(JsonField.of(amount)) + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun amount(amount: JsonField) = apply { this.amount = amount } + + fun currency(currency: Currency) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [Currency] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CurrencyAmount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .amount() + * .currency() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CurrencyAmount = + CurrencyAmount( + checkRequired("amount", amount), + checkRequired("currency", currency), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CurrencyAmount = apply { + if (validated) { + return@apply + } + + amount() + currency().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (amount.asKnown() == null) 0 else 1) + (currency.asKnown()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CurrencyAmount && + amount == other.amount && + currency == other.currency && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(amount, currency, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CurrencyAmount{amount=$amount, currency=$currency, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCancelParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCancelParams.kt new file mode 100644 index 00000000..de602703 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCancelParams.kt @@ -0,0 +1,238 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import java.util.Objects + +/** + * Cancel a pending UMA invitation. Only the inviter or platform can cancel an invitation. + * + * When an invitation is cancelled: + * 1. The invitation status changes from PENDING to CANCELLED + * 2. The invitation can no longer be claimed + * 3. The invitation URL will show as cancelled when accessed + * + * Only pending invitations can be cancelled. Attempting to cancel an invitation that is already + * claimed, expired, or cancelled will result in an error. + */ +class InvitationCancelParams +private constructor( + private val invitationCode: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun invitationCode(): String? = invitationCode + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): InvitationCancelParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [InvitationCancelParams]. */ + fun builder() = Builder() + } + + /** A builder for [InvitationCancelParams]. */ + class Builder internal constructor() { + + private var invitationCode: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + internal fun from(invitationCancelParams: InvitationCancelParams) = apply { + invitationCode = invitationCancelParams.invitationCode + additionalHeaders = invitationCancelParams.additionalHeaders.toBuilder() + additionalQueryParams = invitationCancelParams.additionalQueryParams.toBuilder() + additionalBodyProperties = + invitationCancelParams.additionalBodyProperties.toMutableMap() + } + + fun invitationCode(invitationCode: String?) = apply { this.invitationCode = invitationCode } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [InvitationCancelParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InvitationCancelParams = + InvitationCancelParams( + invitationCode, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Map? = additionalBodyProperties.ifEmpty { null } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> invitationCode ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InvitationCancelParams && + invitationCode == other.invitationCode && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash( + invitationCode, + additionalHeaders, + additionalQueryParams, + additionalBodyProperties, + ) + + override fun toString() = + "InvitationCancelParams{invitationCode=$invitationCode, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationClaimParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationClaimParams.kt new file mode 100644 index 00000000..a8a0e5a6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationClaimParams.kt @@ -0,0 +1,443 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Claim an UMA invitation by associating it with an invitee UMA address. + * + * When an invitation is successfully claimed: + * 1. The invitation status changes from PENDING to CLAIMED + * 2. The invitee UMA address is associated with the invitation + * 3. An INVITATION_CLAIMED webhook is triggered to notify the platform that created the invitation + * + * This endpoint allows customers to accept invitations sent to them by other UMA customers. + */ +class InvitationClaimParams +private constructor( + private val invitationCode: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun invitationCode(): String? = invitationCode + + /** + * The UMA address of the customer claiming the invitation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun inviteeUma(): String = body.inviteeUma() + + /** + * Returns the raw JSON value of [inviteeUma]. + * + * Unlike [inviteeUma], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _inviteeUma(): JsonField = body._inviteeUma() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InvitationClaimParams]. + * + * The following fields are required: + * ```kotlin + * .inviteeUma() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [InvitationClaimParams]. */ + class Builder internal constructor() { + + private var invitationCode: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(invitationClaimParams: InvitationClaimParams) = apply { + invitationCode = invitationClaimParams.invitationCode + body = invitationClaimParams.body.toBuilder() + additionalHeaders = invitationClaimParams.additionalHeaders.toBuilder() + additionalQueryParams = invitationClaimParams.additionalQueryParams.toBuilder() + } + + fun invitationCode(invitationCode: String?) = apply { this.invitationCode = invitationCode } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [inviteeUma] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The UMA address of the customer claiming the invitation */ + fun inviteeUma(inviteeUma: String) = apply { body.inviteeUma(inviteeUma) } + + /** + * Sets [Builder.inviteeUma] to an arbitrary JSON value. + * + * You should usually call [Builder.inviteeUma] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun inviteeUma(inviteeUma: JsonField) = apply { body.inviteeUma(inviteeUma) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [InvitationClaimParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .inviteeUma() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InvitationClaimParams = + InvitationClaimParams( + invitationCode, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> invitationCode ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val inviteeUma: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("inviteeUma") + @ExcludeMissing + inviteeUma: JsonField = JsonMissing.of() + ) : this(inviteeUma, mutableMapOf()) + + /** + * The UMA address of the customer claiming the invitation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun inviteeUma(): String = inviteeUma.getRequired("inviteeUma") + + /** + * Returns the raw JSON value of [inviteeUma]. + * + * Unlike [inviteeUma], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inviteeUma") + @ExcludeMissing + fun _inviteeUma(): JsonField = inviteeUma + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .inviteeUma() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var inviteeUma: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + inviteeUma = body.inviteeUma + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The UMA address of the customer claiming the invitation */ + fun inviteeUma(inviteeUma: String) = inviteeUma(JsonField.of(inviteeUma)) + + /** + * Sets [Builder.inviteeUma] to an arbitrary JSON value. + * + * You should usually call [Builder.inviteeUma] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inviteeUma(inviteeUma: JsonField) = apply { this.inviteeUma = inviteeUma } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .inviteeUma() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body(checkRequired("inviteeUma", inviteeUma), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + inviteeUma() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (inviteeUma.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + inviteeUma == other.inviteeUma && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(inviteeUma, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{inviteeUma=$inviteeUma, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InvitationClaimParams && + invitationCode == other.invitationCode && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(invitationCode, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InvitationClaimParams{invitationCode=$invitationCode, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCreateParams.kt new file mode 100644 index 00000000..7b3be651 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationCreateParams.kt @@ -0,0 +1,656 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +/** Create an UMA invitation from a given platform customer. */ +class InvitationCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The UMA address of the customer creating the invitation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun inviterUma(): String = body.inviterUma() + + /** + * An amount to send (in the smallest unit of the customer's currency) to the invitee when the + * invitation is claimed. This is optional and if not provided, the invitee will not receive any + * amount. Note that the actual sending of the amount must be done by the inviter platform once + * the INVITATION_CLAIMED webhook is received. If the inviter platform either does not send the + * payment or the payment fails, the invitee will not receive this amount. This field is + * primarily used for display purposes on the claiming side of the invitation. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun amountToSend(): Long? = body.amountToSend() + + /** + * When the invitation expires (if at all) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun expiresAt(): OffsetDateTime? = body.expiresAt() + + /** + * First name of the invitee to show as part of the invite + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun firstName(): String? = body.firstName() + + /** + * Returns the raw JSON value of [inviterUma]. + * + * Unlike [inviterUma], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _inviterUma(): JsonField = body._inviterUma() + + /** + * Returns the raw JSON value of [amountToSend]. + * + * Unlike [amountToSend], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _amountToSend(): JsonField = body._amountToSend() + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _expiresAt(): JsonField = body._expiresAt() + + /** + * Returns the raw JSON value of [firstName]. + * + * Unlike [firstName], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _firstName(): JsonField = body._firstName() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InvitationCreateParams]. + * + * The following fields are required: + * ```kotlin + * .inviterUma() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [InvitationCreateParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(invitationCreateParams: InvitationCreateParams) = apply { + body = invitationCreateParams.body.toBuilder() + additionalHeaders = invitationCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = invitationCreateParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [inviterUma] + * - [amountToSend] + * - [expiresAt] + * - [firstName] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The UMA address of the customer creating the invitation */ + fun inviterUma(inviterUma: String) = apply { body.inviterUma(inviterUma) } + + /** + * Sets [Builder.inviterUma] to an arbitrary JSON value. + * + * You should usually call [Builder.inviterUma] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun inviterUma(inviterUma: JsonField) = apply { body.inviterUma(inviterUma) } + + /** + * An amount to send (in the smallest unit of the customer's currency) to the invitee when + * the invitation is claimed. This is optional and if not provided, the invitee will not + * receive any amount. Note that the actual sending of the amount must be done by the + * inviter platform once the INVITATION_CLAIMED webhook is received. If the inviter platform + * either does not send the payment or the payment fails, the invitee will not receive this + * amount. This field is primarily used for display purposes on the claiming side of the + * invitation. + */ + fun amountToSend(amountToSend: Long) = apply { body.amountToSend(amountToSend) } + + /** + * Sets [Builder.amountToSend] to an arbitrary JSON value. + * + * You should usually call [Builder.amountToSend] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun amountToSend(amountToSend: JsonField) = apply { body.amountToSend(amountToSend) } + + /** When the invitation expires (if at all) */ + fun expiresAt(expiresAt: OffsetDateTime) = apply { body.expiresAt(expiresAt) } + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { body.expiresAt(expiresAt) } + + /** First name of the invitee to show as part of the invite */ + fun firstName(firstName: String) = apply { body.firstName(firstName) } + + /** + * Sets [Builder.firstName] to an arbitrary JSON value. + * + * You should usually call [Builder.firstName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun firstName(firstName: JsonField) = apply { body.firstName(firstName) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [InvitationCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .inviterUma() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InvitationCreateParams = + InvitationCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val inviterUma: JsonField, + private val amountToSend: JsonField, + private val expiresAt: JsonField, + private val firstName: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("inviterUma") + @ExcludeMissing + inviterUma: JsonField = JsonMissing.of(), + @JsonProperty("amountToSend") + @ExcludeMissing + amountToSend: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("firstName") + @ExcludeMissing + firstName: JsonField = JsonMissing.of(), + ) : this(inviterUma, amountToSend, expiresAt, firstName, mutableMapOf()) + + /** + * The UMA address of the customer creating the invitation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun inviterUma(): String = inviterUma.getRequired("inviterUma") + + /** + * An amount to send (in the smallest unit of the customer's currency) to the invitee when + * the invitation is claimed. This is optional and if not provided, the invitee will not + * receive any amount. Note that the actual sending of the amount must be done by the + * inviter platform once the INVITATION_CLAIMED webhook is received. If the inviter platform + * either does not send the payment or the payment fails, the invitee will not receive this + * amount. This field is primarily used for display purposes on the claiming side of the + * invitation. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun amountToSend(): Long? = amountToSend.getNullable("amountToSend") + + /** + * When the invitation expires (if at all) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): OffsetDateTime? = expiresAt.getNullable("expiresAt") + + /** + * First name of the invitee to show as part of the invite + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun firstName(): String? = firstName.getNullable("firstName") + + /** + * Returns the raw JSON value of [inviterUma]. + * + * Unlike [inviterUma], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inviterUma") + @ExcludeMissing + fun _inviterUma(): JsonField = inviterUma + + /** + * Returns the raw JSON value of [amountToSend]. + * + * Unlike [amountToSend], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("amountToSend") + @ExcludeMissing + fun _amountToSend(): JsonField = amountToSend + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [firstName]. + * + * Unlike [firstName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("firstName") @ExcludeMissing fun _firstName(): JsonField = firstName + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .inviterUma() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var inviterUma: JsonField? = null + private var amountToSend: JsonField = JsonMissing.of() + private var expiresAt: JsonField = JsonMissing.of() + private var firstName: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + inviterUma = body.inviterUma + amountToSend = body.amountToSend + expiresAt = body.expiresAt + firstName = body.firstName + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The UMA address of the customer creating the invitation */ + fun inviterUma(inviterUma: String) = inviterUma(JsonField.of(inviterUma)) + + /** + * Sets [Builder.inviterUma] to an arbitrary JSON value. + * + * You should usually call [Builder.inviterUma] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inviterUma(inviterUma: JsonField) = apply { this.inviterUma = inviterUma } + + /** + * An amount to send (in the smallest unit of the customer's currency) to the invitee + * when the invitation is claimed. This is optional and if not provided, the invitee + * will not receive any amount. Note that the actual sending of the amount must be done + * by the inviter platform once the INVITATION_CLAIMED webhook is received. If the + * inviter platform either does not send the payment or the payment fails, the invitee + * will not receive this amount. This field is primarily used for display purposes on + * the claiming side of the invitation. + */ + fun amountToSend(amountToSend: Long) = amountToSend(JsonField.of(amountToSend)) + + /** + * Sets [Builder.amountToSend] to an arbitrary JSON value. + * + * You should usually call [Builder.amountToSend] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun amountToSend(amountToSend: JsonField) = apply { + this.amountToSend = amountToSend + } + + /** When the invitation expires (if at all) */ + fun expiresAt(expiresAt: OffsetDateTime) = expiresAt(JsonField.of(expiresAt)) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { + this.expiresAt = expiresAt + } + + /** First name of the invitee to show as part of the invite */ + fun firstName(firstName: String) = firstName(JsonField.of(firstName)) + + /** + * Sets [Builder.firstName] to an arbitrary JSON value. + * + * You should usually call [Builder.firstName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun firstName(firstName: JsonField) = apply { this.firstName = firstName } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .inviterUma() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("inviterUma", inviterUma), + amountToSend, + expiresAt, + firstName, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + inviterUma() + amountToSend() + expiresAt() + firstName() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (inviterUma.asKnown() == null) 0 else 1) + + (if (amountToSend.asKnown() == null) 0 else 1) + + (if (expiresAt.asKnown() == null) 0 else 1) + + (if (firstName.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + inviterUma == other.inviterUma && + amountToSend == other.amountToSend && + expiresAt == other.expiresAt && + firstName == other.firstName && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(inviterUma, amountToSend, expiresAt, firstName, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{inviterUma=$inviterUma, amountToSend=$amountToSend, expiresAt=$expiresAt, firstName=$firstName, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InvitationCreateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InvitationCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationRetrieveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationRetrieveParams.kt new file mode 100644 index 00000000..2d9bda8e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/InvitationRetrieveParams.kt @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** Get a specific UMA invitation by code. */ +class InvitationRetrieveParams +private constructor( + private val invitationCode: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun invitationCode(): String? = invitationCode + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): InvitationRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [InvitationRetrieveParams]. */ + fun builder() = Builder() + } + + /** A builder for [InvitationRetrieveParams]. */ + class Builder internal constructor() { + + private var invitationCode: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(invitationRetrieveParams: InvitationRetrieveParams) = apply { + invitationCode = invitationRetrieveParams.invitationCode + additionalHeaders = invitationRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = invitationRetrieveParams.additionalQueryParams.toBuilder() + } + + fun invitationCode(invitationCode: String?) = apply { this.invitationCode = invitationCode } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [InvitationRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InvitationRetrieveParams = + InvitationRetrieveParams( + invitationCode, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> invitationCode ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InvitationRetrieveParams && + invitationCode == other.invitationCode && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(invitationCode, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InvitationRetrieveParams{invitationCode=$invitationCode, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/UmaInvitation.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/UmaInvitation.kt new file mode 100644 index 00000000..1f11c584 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/invitations/UmaInvitation.kt @@ -0,0 +1,698 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class UmaInvitation +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val code: JsonField, + private val createdAt: JsonField, + private val inviterUma: JsonField, + private val status: JsonField, + private val url: JsonField, + private val amountToSend: JsonField, + private val claimedAt: JsonField, + private val expiresAt: JsonField, + private val firstName: JsonField, + private val inviteeUma: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("code") @ExcludeMissing code: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("inviterUma") + @ExcludeMissing + inviterUma: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), + @JsonProperty("amountToSend") + @ExcludeMissing + amountToSend: JsonField = JsonMissing.of(), + @JsonProperty("claimedAt") + @ExcludeMissing + claimedAt: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("firstName") @ExcludeMissing firstName: JsonField = JsonMissing.of(), + @JsonProperty("inviteeUma") @ExcludeMissing inviteeUma: JsonField = JsonMissing.of(), + ) : this( + code, + createdAt, + inviterUma, + status, + url, + amountToSend, + claimedAt, + expiresAt, + firstName, + inviteeUma, + mutableMapOf(), + ) + + /** + * The unique code of the invitation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun code(): String = code.getRequired("code") + + /** + * When the invitation was created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime = createdAt.getRequired("createdAt") + + /** + * The UMA address of the inviter + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun inviterUma(): String = inviterUma.getRequired("inviterUma") + + /** + * The status of the invitation + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): Status = status.getRequired("status") + + /** + * The URL where this invitation can be claimed. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun url(): String = url.getRequired("url") + + /** + * The amount to send to the invitee when the invitation is claimed. This is optional and if not + * provided, the invitee will not receive any amount. Note that the actual sending of the amount + * must be done by the inviter platform once the INVITATION_CLAIMED webhook is received. If the + * inviter platform either does not send the payment or the payment fails, the invitee will not + * receive this amount. This field is primarily used for display purposes on the claiming side + * of the invitation. This field is useful for "send-by-link" style customer flows where an + * inviter can send a payment simply by sharing a link without knowing the receiver's UMA + * address. Note that these sends can only be sender-locked, meaning that the sender will not + * know ahead of time how much the receiver will receive in the receiving currency. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun amountToSend(): CurrencyAmount? = amountToSend.getNullable("amountToSend") + + /** + * When the invitation was claimed if it has been claimed + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun claimedAt(): OffsetDateTime? = claimedAt.getNullable("claimedAt") + + /** + * When the invitation expires (if at all) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun expiresAt(): OffsetDateTime? = expiresAt.getNullable("expiresAt") + + /** + * The inviter's first name. Will be displayed when the recipient clicks the invite link + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun firstName(): String? = firstName.getNullable("firstName") + + /** + * The UMA address of the invitee + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun inviteeUma(): String? = inviteeUma.getNullable("inviteeUma") + + /** + * Returns the raw JSON value of [code]. + * + * Unlike [code], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("code") @ExcludeMissing fun _code(): JsonField = code + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [inviterUma]. + * + * Unlike [inviterUma], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inviterUma") @ExcludeMissing fun _inviterUma(): JsonField = inviterUma + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + /** + * Returns the raw JSON value of [amountToSend]. + * + * Unlike [amountToSend], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amountToSend") + @ExcludeMissing + fun _amountToSend(): JsonField = amountToSend + + /** + * Returns the raw JSON value of [claimedAt]. + * + * Unlike [claimedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("claimedAt") + @ExcludeMissing + fun _claimedAt(): JsonField = claimedAt + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [firstName]. + * + * Unlike [firstName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("firstName") @ExcludeMissing fun _firstName(): JsonField = firstName + + /** + * Returns the raw JSON value of [inviteeUma]. + * + * Unlike [inviteeUma], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inviteeUma") @ExcludeMissing fun _inviteeUma(): JsonField = inviteeUma + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaInvitation]. + * + * The following fields are required: + * ```kotlin + * .code() + * .createdAt() + * .inviterUma() + * .status() + * .url() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaInvitation]. */ + class Builder internal constructor() { + + private var code: JsonField? = null + private var createdAt: JsonField? = null + private var inviterUma: JsonField? = null + private var status: JsonField? = null + private var url: JsonField? = null + private var amountToSend: JsonField = JsonMissing.of() + private var claimedAt: JsonField = JsonMissing.of() + private var expiresAt: JsonField = JsonMissing.of() + private var firstName: JsonField = JsonMissing.of() + private var inviteeUma: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(umaInvitation: UmaInvitation) = apply { + code = umaInvitation.code + createdAt = umaInvitation.createdAt + inviterUma = umaInvitation.inviterUma + status = umaInvitation.status + url = umaInvitation.url + amountToSend = umaInvitation.amountToSend + claimedAt = umaInvitation.claimedAt + expiresAt = umaInvitation.expiresAt + firstName = umaInvitation.firstName + inviteeUma = umaInvitation.inviteeUma + additionalProperties = umaInvitation.additionalProperties.toMutableMap() + } + + /** The unique code of the invitation */ + fun code(code: String) = code(JsonField.of(code)) + + /** + * Sets [Builder.code] to an arbitrary JSON value. + * + * You should usually call [Builder.code] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun code(code: JsonField) = apply { this.code = code } + + /** When the invitation was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** The UMA address of the inviter */ + fun inviterUma(inviterUma: String) = inviterUma(JsonField.of(inviterUma)) + + /** + * Sets [Builder.inviterUma] to an arbitrary JSON value. + * + * You should usually call [Builder.inviterUma] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun inviterUma(inviterUma: JsonField) = apply { this.inviterUma = inviterUma } + + /** The status of the invitation */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** The URL where this invitation can be claimed. */ + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun url(url: JsonField) = apply { this.url = url } + + /** + * The amount to send to the invitee when the invitation is claimed. This is optional and if + * not provided, the invitee will not receive any amount. Note that the actual sending of + * the amount must be done by the inviter platform once the INVITATION_CLAIMED webhook is + * received. If the inviter platform either does not send the payment or the payment fails, + * the invitee will not receive this amount. This field is primarily used for display + * purposes on the claiming side of the invitation. This field is useful for "send-by-link" + * style customer flows where an inviter can send a payment simply by sharing a link without + * knowing the receiver's UMA address. Note that these sends can only be sender-locked, + * meaning that the sender will not know ahead of time how much the receiver will receive in + * the receiving currency. + */ + fun amountToSend(amountToSend: CurrencyAmount) = amountToSend(JsonField.of(amountToSend)) + + /** + * Sets [Builder.amountToSend] to an arbitrary JSON value. + * + * You should usually call [Builder.amountToSend] with a well-typed [CurrencyAmount] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun amountToSend(amountToSend: JsonField) = apply { + this.amountToSend = amountToSend + } + + /** When the invitation was claimed if it has been claimed */ + fun claimedAt(claimedAt: OffsetDateTime) = claimedAt(JsonField.of(claimedAt)) + + /** + * Sets [Builder.claimedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.claimedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun claimedAt(claimedAt: JsonField) = apply { this.claimedAt = claimedAt } + + /** When the invitation expires (if at all) */ + fun expiresAt(expiresAt: OffsetDateTime) = expiresAt(JsonField.of(expiresAt)) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** The inviter's first name. Will be displayed when the recipient clicks the invite link */ + fun firstName(firstName: String) = firstName(JsonField.of(firstName)) + + /** + * Sets [Builder.firstName] to an arbitrary JSON value. + * + * You should usually call [Builder.firstName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun firstName(firstName: JsonField) = apply { this.firstName = firstName } + + /** The UMA address of the invitee */ + fun inviteeUma(inviteeUma: String) = inviteeUma(JsonField.of(inviteeUma)) + + /** + * Sets [Builder.inviteeUma] to an arbitrary JSON value. + * + * You should usually call [Builder.inviteeUma] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun inviteeUma(inviteeUma: JsonField) = apply { this.inviteeUma = inviteeUma } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UmaInvitation]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .code() + * .createdAt() + * .inviterUma() + * .status() + * .url() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaInvitation = + UmaInvitation( + checkRequired("code", code), + checkRequired("createdAt", createdAt), + checkRequired("inviterUma", inviterUma), + checkRequired("status", status), + checkRequired("url", url), + amountToSend, + claimedAt, + expiresAt, + firstName, + inviteeUma, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UmaInvitation = apply { + if (validated) { + return@apply + } + + code() + createdAt() + inviterUma() + status().validate() + url() + amountToSend()?.validate() + claimedAt() + expiresAt() + firstName() + inviteeUma() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (code.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (inviterUma.asKnown() == null) 0 else 1) + + (status.asKnown()?.validity() ?: 0) + + (if (url.asKnown() == null) 0 else 1) + + (amountToSend.asKnown()?.validity() ?: 0) + + (if (claimedAt.asKnown() == null) 0 else 1) + + (if (expiresAt.asKnown() == null) 0 else 1) + + (if (firstName.asKnown() == null) 0 else 1) + + (if (inviteeUma.asKnown() == null) 0 else 1) + + /** The status of the invitation */ + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PENDING = of("PENDING") + + val CLAIMED = of("CLAIMED") + + val EXPIRED = of("EXPIRED") + + val CANCELLED = of("CANCELLED") + + fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + PENDING, + CLAIMED, + EXPIRED, + CANCELLED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PENDING, + CLAIMED, + EXPIRED, + CANCELLED, + /** An enum member indicating that [Status] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PENDING -> Value.PENDING + CLAIMED -> Value.CLAIMED + EXPIRED -> Value.EXPIRED + CANCELLED -> Value.CANCELLED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + PENDING -> Known.PENDING + CLAIMED -> Known.CLAIMED + EXPIRED -> Known.EXPIRED + CANCELLED -> Known.CANCELLED + else -> throw GridInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaInvitation && + code == other.code && + createdAt == other.createdAt && + inviterUma == other.inviterUma && + status == other.status && + url == other.url && + amountToSend == other.amountToSend && + claimedAt == other.claimedAt && + expiresAt == other.expiresAt && + firstName == other.firstName && + inviteeUma == other.inviteeUma && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + code, + createdAt, + inviterUma, + status, + url, + amountToSend, + claimedAt, + expiresAt, + firstName, + inviteeUma, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UmaInvitation{code=$code, createdAt=$createdAt, inviterUma=$inviterUma, status=$status, url=$url, amountToSend=$amountToSend, claimedAt=$claimedAt, expiresAt=$expiresAt, firstName=$firstName, inviteeUma=$inviteeUma, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParams.kt new file mode 100644 index 00000000..65d1f684 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParams.kt @@ -0,0 +1,433 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.plaid + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Creates a Plaid Link token that can be used to initialize Plaid Link in your application. The + * Link token is used to authenticate the customer and allow them to select their bank account. + * + * **Async Flow:** + * 1. Platform calls this endpoint to get a link_token and callbackUrl + * 2. Platform displays Plaid Link UI to the end customer using the link_token + * 3. End customer authenticates with their bank and selects an account + * 4. Plaid returns a public_token to the platform + * 5. Platform POSTs the public_token to the callbackUrl + * 6. Lightspark exchanges the public_token with Plaid and creates the external account + * asynchronously + * 7. Platform receives a webhook notification when the external account is ready + */ +class PlaidCreateLinkTokenParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The ID of the customer for whom to create the Plaid Link token and external account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerId(): String = body.customerId() + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _customerId(): JsonField = body._customerId() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PlaidCreateLinkTokenParams]. + * + * The following fields are required: + * ```kotlin + * .customerId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PlaidCreateLinkTokenParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(plaidCreateLinkTokenParams: PlaidCreateLinkTokenParams) = apply { + body = plaidCreateLinkTokenParams.body.toBuilder() + additionalHeaders = plaidCreateLinkTokenParams.additionalHeaders.toBuilder() + additionalQueryParams = plaidCreateLinkTokenParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [customerId] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The ID of the customer for whom to create the Plaid Link token and external account */ + fun customerId(customerId: String) = apply { body.customerId(customerId) } + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { body.customerId(customerId) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [PlaidCreateLinkTokenParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .customerId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PlaidCreateLinkTokenParams = + PlaidCreateLinkTokenParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val customerId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of() + ) : this(customerId, mutableMapOf()) + + /** + * The ID of the customer for whom to create the Plaid Link token and external account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerId(): String = customerId.getRequired("customerId") + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") + @ExcludeMissing + fun _customerId(): JsonField = customerId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .customerId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var customerId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + customerId = body.customerId + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * The ID of the customer for whom to create the Plaid Link token and external account + */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .customerId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body(checkRequired("customerId", customerId), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + customerId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (customerId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + customerId == other.customerId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(customerId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{customerId=$customerId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlaidCreateLinkTokenParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "PlaidCreateLinkTokenParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponse.kt new file mode 100644 index 00000000..96e7d6fb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponse.kt @@ -0,0 +1,308 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.plaid + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class PlaidCreateLinkTokenResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val callbackUrl: JsonField, + private val expiration: JsonField, + private val linkToken: JsonField, + private val requestId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("callbackUrl") + @ExcludeMissing + callbackUrl: JsonField = JsonMissing.of(), + @JsonProperty("expiration") + @ExcludeMissing + expiration: JsonField = JsonMissing.of(), + @JsonProperty("linkToken") @ExcludeMissing linkToken: JsonField = JsonMissing.of(), + @JsonProperty("requestId") @ExcludeMissing requestId: JsonField = JsonMissing.of(), + ) : this(callbackUrl, expiration, linkToken, requestId, mutableMapOf()) + + /** + * The URL where the platform should POST the public_token after the customer completes Plaid + * Link authentication. This will trigger asynchronous external account creation. The URL + * includes the linkToken as the path parameter. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun callbackUrl(): String = callbackUrl.getRequired("callbackUrl") + + /** + * The ISO 8601 timestamp when this link token expires. Link tokens typically expire after 4 + * hours. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun expiration(): OffsetDateTime = expiration.getRequired("expiration") + + /** + * The Plaid Link token to be used to initialize Plaid Link in your application. This token is + * single-use and expires after the specified expiration time. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun linkToken(): String = linkToken.getRequired("linkToken") + + /** + * A unique identifier for this request, useful for debugging + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun requestId(): String? = requestId.getNullable("requestId") + + /** + * Returns the raw JSON value of [callbackUrl]. + * + * Unlike [callbackUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("callbackUrl") @ExcludeMissing fun _callbackUrl(): JsonField = callbackUrl + + /** + * Returns the raw JSON value of [expiration]. + * + * Unlike [expiration], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiration") + @ExcludeMissing + fun _expiration(): JsonField = expiration + + /** + * Returns the raw JSON value of [linkToken]. + * + * Unlike [linkToken], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("linkToken") @ExcludeMissing fun _linkToken(): JsonField = linkToken + + /** + * Returns the raw JSON value of [requestId]. + * + * Unlike [requestId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("requestId") @ExcludeMissing fun _requestId(): JsonField = requestId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PlaidCreateLinkTokenResponse]. + * + * The following fields are required: + * ```kotlin + * .callbackUrl() + * .expiration() + * .linkToken() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PlaidCreateLinkTokenResponse]. */ + class Builder internal constructor() { + + private var callbackUrl: JsonField? = null + private var expiration: JsonField? = null + private var linkToken: JsonField? = null + private var requestId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(plaidCreateLinkTokenResponse: PlaidCreateLinkTokenResponse) = apply { + callbackUrl = plaidCreateLinkTokenResponse.callbackUrl + expiration = plaidCreateLinkTokenResponse.expiration + linkToken = plaidCreateLinkTokenResponse.linkToken + requestId = plaidCreateLinkTokenResponse.requestId + additionalProperties = plaidCreateLinkTokenResponse.additionalProperties.toMutableMap() + } + + /** + * The URL where the platform should POST the public_token after the customer completes + * Plaid Link authentication. This will trigger asynchronous external account creation. The + * URL includes the linkToken as the path parameter. + */ + fun callbackUrl(callbackUrl: String) = callbackUrl(JsonField.of(callbackUrl)) + + /** + * Sets [Builder.callbackUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.callbackUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun callbackUrl(callbackUrl: JsonField) = apply { this.callbackUrl = callbackUrl } + + /** + * The ISO 8601 timestamp when this link token expires. Link tokens typically expire after 4 + * hours. + */ + fun expiration(expiration: OffsetDateTime) = expiration(JsonField.of(expiration)) + + /** + * Sets [Builder.expiration] to an arbitrary JSON value. + * + * You should usually call [Builder.expiration] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiration(expiration: JsonField) = apply { + this.expiration = expiration + } + + /** + * The Plaid Link token to be used to initialize Plaid Link in your application. This token + * is single-use and expires after the specified expiration time. + */ + fun linkToken(linkToken: String) = linkToken(JsonField.of(linkToken)) + + /** + * Sets [Builder.linkToken] to an arbitrary JSON value. + * + * You should usually call [Builder.linkToken] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun linkToken(linkToken: JsonField) = apply { this.linkToken = linkToken } + + /** A unique identifier for this request, useful for debugging */ + fun requestId(requestId: String) = requestId(JsonField.of(requestId)) + + /** + * Sets [Builder.requestId] to an arbitrary JSON value. + * + * You should usually call [Builder.requestId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun requestId(requestId: JsonField) = apply { this.requestId = requestId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PlaidCreateLinkTokenResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .callbackUrl() + * .expiration() + * .linkToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PlaidCreateLinkTokenResponse = + PlaidCreateLinkTokenResponse( + checkRequired("callbackUrl", callbackUrl), + checkRequired("expiration", expiration), + checkRequired("linkToken", linkToken), + requestId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PlaidCreateLinkTokenResponse = apply { + if (validated) { + return@apply + } + + callbackUrl() + expiration() + linkToken() + requestId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (callbackUrl.asKnown() == null) 0 else 1) + + (if (expiration.asKnown() == null) 0 else 1) + + (if (linkToken.asKnown() == null) 0 else 1) + + (if (requestId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlaidCreateLinkTokenResponse && + callbackUrl == other.callbackUrl && + expiration == other.expiration && + linkToken == other.linkToken && + requestId == other.requestId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(callbackUrl, expiration, linkToken, requestId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PlaidCreateLinkTokenResponse{callbackUrl=$callbackUrl, expiration=$expiration, linkToken=$linkToken, requestId=$requestId, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParams.kt new file mode 100644 index 00000000..17eb89f9 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParams.kt @@ -0,0 +1,532 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.plaid + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * After the customer completes Plaid Link authentication, the platform should POST the public_token + * to this callback URL (provided in the link token response). + * + * This will trigger asynchronous processing: + * 1. Lightspark exchanges the public_token for an access_token with Plaid + * 2. Lightspark retrieves and verifies the account details + * 3. An external account is created + * 4. A webhook notification is sent to the platform when complete + */ +class PlaidSubmitPublicTokenParams +private constructor( + private val plaidLinkToken: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun plaidLinkToken(): String? = plaidLinkToken + + /** + * The public token returned by Plaid Link after the customer successfully authenticates and + * selects an account. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun publicToken(): String = body.publicToken() + + /** + * Optional Plaid account ID if the customer selected a specific account. If not provided, the + * default account will be used. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun accountId(): String? = body.accountId() + + /** + * Returns the raw JSON value of [publicToken]. + * + * Unlike [publicToken], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _publicToken(): JsonField = body._publicToken() + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _accountId(): JsonField = body._accountId() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PlaidSubmitPublicTokenParams]. + * + * The following fields are required: + * ```kotlin + * .publicToken() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PlaidSubmitPublicTokenParams]. */ + class Builder internal constructor() { + + private var plaidLinkToken: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(plaidSubmitPublicTokenParams: PlaidSubmitPublicTokenParams) = apply { + plaidLinkToken = plaidSubmitPublicTokenParams.plaidLinkToken + body = plaidSubmitPublicTokenParams.body.toBuilder() + additionalHeaders = plaidSubmitPublicTokenParams.additionalHeaders.toBuilder() + additionalQueryParams = plaidSubmitPublicTokenParams.additionalQueryParams.toBuilder() + } + + fun plaidLinkToken(plaidLinkToken: String?) = apply { this.plaidLinkToken = plaidLinkToken } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [publicToken] + * - [accountId] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * The public token returned by Plaid Link after the customer successfully authenticates and + * selects an account. + */ + fun publicToken(publicToken: String) = apply { body.publicToken(publicToken) } + + /** + * Sets [Builder.publicToken] to an arbitrary JSON value. + * + * You should usually call [Builder.publicToken] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun publicToken(publicToken: JsonField) = apply { body.publicToken(publicToken) } + + /** + * Optional Plaid account ID if the customer selected a specific account. If not provided, + * the default account will be used. + */ + fun accountId(accountId: String) = apply { body.accountId(accountId) } + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun accountId(accountId: JsonField) = apply { body.accountId(accountId) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [PlaidSubmitPublicTokenParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .publicToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PlaidSubmitPublicTokenParams = + PlaidSubmitPublicTokenParams( + plaidLinkToken, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> plaidLinkToken ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val publicToken: JsonField, + private val accountId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("publicToken") + @ExcludeMissing + publicToken: JsonField = JsonMissing.of(), + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of(), + ) : this(publicToken, accountId, mutableMapOf()) + + /** + * The public token returned by Plaid Link after the customer successfully authenticates and + * selects an account. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun publicToken(): String = publicToken.getRequired("publicToken") + + /** + * Optional Plaid account ID if the customer selected a specific account. If not provided, + * the default account will be used. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun accountId(): String? = accountId.getNullable("accountId") + + /** + * Returns the raw JSON value of [publicToken]. + * + * Unlike [publicToken], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("publicToken") + @ExcludeMissing + fun _publicToken(): JsonField = publicToken + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .publicToken() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var publicToken: JsonField? = null + private var accountId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + publicToken = body.publicToken + accountId = body.accountId + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * The public token returned by Plaid Link after the customer successfully authenticates + * and selects an account. + */ + fun publicToken(publicToken: String) = publicToken(JsonField.of(publicToken)) + + /** + * Sets [Builder.publicToken] to an arbitrary JSON value. + * + * You should usually call [Builder.publicToken] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun publicToken(publicToken: JsonField) = apply { + this.publicToken = publicToken + } + + /** + * Optional Plaid account ID if the customer selected a specific account. If not + * provided, the default account will be used. + */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .publicToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("publicToken", publicToken), + accountId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + publicToken() + accountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (publicToken.asKnown() == null) 0 else 1) + + (if (accountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + publicToken == other.publicToken && + accountId == other.accountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(publicToken, accountId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{publicToken=$publicToken, accountId=$accountId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlaidSubmitPublicTokenParams && + plaidLinkToken == other.plaidLinkToken && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(plaidLinkToken, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "PlaidSubmitPublicTokenParams{plaidLinkToken=$plaidLinkToken, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParams.kt new file mode 100644 index 00000000..61d16790 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParams.kt @@ -0,0 +1,201 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Retrieve a list of all internal accounts that belong to the platform, as opposed to an individual + * customer. + * + * These accounts are created automatically when the platform is configured for each supported + * currency. They can be used for things like distributing bitcoin rewards to customers, or for + * other platform-wide purposes. + */ +class PlatformListInternalAccountsParams +private constructor( + private val currency: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter by currency code */ + fun currency(): String? = currency + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): PlatformListInternalAccountsParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of + * [PlatformListInternalAccountsParams]. + */ + fun builder() = Builder() + } + + /** A builder for [PlatformListInternalAccountsParams]. */ + class Builder internal constructor() { + + private var currency: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(platformListInternalAccountsParams: PlatformListInternalAccountsParams) = + apply { + currency = platformListInternalAccountsParams.currency + additionalHeaders = platformListInternalAccountsParams.additionalHeaders.toBuilder() + additionalQueryParams = + platformListInternalAccountsParams.additionalQueryParams.toBuilder() + } + + /** Filter by currency code */ + fun currency(currency: String?) = apply { this.currency = currency } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [PlatformListInternalAccountsParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): PlatformListInternalAccountsParams = + PlatformListInternalAccountsParams( + currency, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + currency?.let { put("currency", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlatformListInternalAccountsParams && + currency == other.currency && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(currency, additionalHeaders, additionalQueryParams) + + override fun toString() = + "PlatformListInternalAccountsParams{currency=$currency, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponse.kt new file mode 100644 index 00000000..35b2c802 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponse.kt @@ -0,0 +1,196 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import java.util.Collections +import java.util.Objects + +class PlatformListInternalAccountsResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") + @ExcludeMissing + data: JsonField> = JsonMissing.of() + ) : this(data, mutableMapOf()) + + /** + * List of internal accounts matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [PlatformListInternalAccountsResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PlatformListInternalAccountsResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from( + platformListInternalAccountsResponse: PlatformListInternalAccountsResponse + ) = apply { + data = platformListInternalAccountsResponse.data.map { it.toMutableList() } + additionalProperties = + platformListInternalAccountsResponse.additionalProperties.toMutableMap() + } + + /** List of internal accounts matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [InternalAccount] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: InternalAccount) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PlatformListInternalAccountsResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PlatformListInternalAccountsResponse = + PlatformListInternalAccountsResponse( + checkRequired("data", data).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PlatformListInternalAccountsResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PlatformListInternalAccountsResponse && + data == other.data && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PlatformListInternalAccountsResponse{data=$data, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParams.kt new file mode 100644 index 00000000..dc0e6460 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParams.kt @@ -0,0 +1,216 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform.externalaccounts + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import java.util.Objects + +/** + * Register a new external bank account for the platform. + * + * **Sandbox Testing:** In sandbox mode, use these account number patterns to test different + * transfer scenarios. These patterns should be used with the primary alias, address, or identifier + * of whatever account type you're testing. For example, the US account number, a CLABE, an IBAN, a + * spark wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ +class ExternalAccountCreateParams +private constructor( + private val externalAccountCreate: ExternalAccountCreate, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun externalAccountCreate(): ExternalAccountCreate = externalAccountCreate + + fun _additionalBodyProperties(): Map = + externalAccountCreate._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountCreateParams]. + * + * The following fields are required: + * ```kotlin + * .externalAccountCreate() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountCreateParams]. */ + class Builder internal constructor() { + + private var externalAccountCreate: ExternalAccountCreate? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(externalAccountCreateParams: ExternalAccountCreateParams) = apply { + externalAccountCreate = externalAccountCreateParams.externalAccountCreate + additionalHeaders = externalAccountCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = externalAccountCreateParams.additionalQueryParams.toBuilder() + } + + fun externalAccountCreate(externalAccountCreate: ExternalAccountCreate) = apply { + this.externalAccountCreate = externalAccountCreate + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExternalAccountCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .externalAccountCreate() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountCreateParams = + ExternalAccountCreateParams( + checkRequired("externalAccountCreate", externalAccountCreate), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): ExternalAccountCreate = externalAccountCreate + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountCreateParams && + externalAccountCreate == other.externalAccountCreate && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(externalAccountCreate, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ExternalAccountCreateParams{externalAccountCreate=$externalAccountCreate, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParams.kt new file mode 100644 index 00000000..5823e2a3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParams.kt @@ -0,0 +1,197 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform.externalaccounts + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Retrieve a list of all external accounts that belong to the platform, as opposed to an individual + * customer. + * + * These accounts are used for platform-wide operations such as receiving funds from external + * sources or managing platform-level payment destinations. + */ +class ExternalAccountListParams +private constructor( + private val currency: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter by currency code */ + fun currency(): String? = currency + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ExternalAccountListParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountListParams]. + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountListParams]. */ + class Builder internal constructor() { + + private var currency: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(externalAccountListParams: ExternalAccountListParams) = apply { + currency = externalAccountListParams.currency + additionalHeaders = externalAccountListParams.additionalHeaders.toBuilder() + additionalQueryParams = externalAccountListParams.additionalQueryParams.toBuilder() + } + + /** Filter by currency code */ + fun currency(currency: String?) = apply { this.currency = currency } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ExternalAccountListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ExternalAccountListParams = + ExternalAccountListParams( + currency, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + currency?.let { put("currency", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountListParams && + currency == other.currency && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(currency, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ExternalAccountListParams{currency=$currency, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponse.kt new file mode 100644 index 00000000..f590aefa --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponse.kt @@ -0,0 +1,192 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform.externalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import java.util.Collections +import java.util.Objects + +class ExternalAccountListResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") + @ExcludeMissing + data: JsonField> = JsonMissing.of() + ) : this(data, mutableMapOf()) + + /** + * List of external accounts matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountListResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountListResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(externalAccountListResponse: ExternalAccountListResponse) = apply { + data = externalAccountListResponse.data.map { it.toMutableList() } + additionalProperties = externalAccountListResponse.additionalProperties.toMutableMap() + } + + /** List of external accounts matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [ExternalAccount] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: ExternalAccount) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ExternalAccountListResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountListResponse = + ExternalAccountListResponse( + checkRequired("data", data).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ExternalAccountListResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountListResponse && + data == other.data && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ExternalAccountListResponse{data=$data, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseDestination.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseDestination.kt new file mode 100644 index 00000000..20b69eb1 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseDestination.kt @@ -0,0 +1,112 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class BaseDestination +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor(private val additionalProperties: MutableMap) { + + @JsonCreator private constructor() : this(mutableMapOf()) + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [BaseDestination]. */ + fun builder() = Builder() + } + + /** A builder for [BaseDestination]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(baseDestination: BaseDestination) = apply { + additionalProperties = baseDestination.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BaseDestination]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BaseDestination = BaseDestination(additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): BaseDestination = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = 0 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BaseDestination && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "BaseDestination{additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfo.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfo.kt new file mode 100644 index 00000000..166d8b4a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfo.kt @@ -0,0 +1,113 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class BasePaymentAccountInfo +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor(private val additionalProperties: MutableMap) { + + @JsonCreator private constructor() : this(mutableMapOf()) + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [BasePaymentAccountInfo]. */ + fun builder() = Builder() + } + + /** A builder for [BasePaymentAccountInfo]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(basePaymentAccountInfo: BasePaymentAccountInfo) = apply { + additionalProperties = basePaymentAccountInfo.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BasePaymentAccountInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BasePaymentAccountInfo = + BasePaymentAccountInfo(additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): BasePaymentAccountInfo = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = 0 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BasePaymentAccountInfo && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "BasePaymentAccountInfo{additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseQuoteSource.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseQuoteSource.kt new file mode 100644 index 00000000..9350c3d9 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/BaseQuoteSource.kt @@ -0,0 +1,112 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class BaseQuoteSource +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor(private val additionalProperties: MutableMap) { + + @JsonCreator private constructor() : this(mutableMapOf()) + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [BaseQuoteSource]. */ + fun builder() = Builder() + } + + /** A builder for [BaseQuoteSource]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(baseQuoteSource: BaseQuoteSource) = apply { + additionalProperties = baseQuoteSource.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BaseQuoteSource]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BaseQuoteSource = BaseQuoteSource(additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): BaseQuoteSource = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = 0 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BaseQuoteSource && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "BaseQuoteSource{additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Currency.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Currency.kt new file mode 100644 index 00000000..3f630f7a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Currency.kt @@ -0,0 +1,260 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class Currency +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val code: JsonField, + private val decimals: JsonField, + private val name: JsonField, + private val symbol: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("code") @ExcludeMissing code: JsonField = JsonMissing.of(), + @JsonProperty("decimals") @ExcludeMissing decimals: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("symbol") @ExcludeMissing symbol: JsonField = JsonMissing.of(), + ) : this(code, decimals, name, symbol, mutableMapOf()) + + /** + * Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use + * their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun code(): String? = code.getNullable("code") + + /** + * Number of decimal places for the currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun decimals(): Long? = decimals.getNullable("decimals") + + /** + * Full name of the currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun name(): String? = name.getNullable("name") + + /** + * Symbol of the currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun symbol(): String? = symbol.getNullable("symbol") + + /** + * Returns the raw JSON value of [code]. + * + * Unlike [code], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("code") @ExcludeMissing fun _code(): JsonField = code + + /** + * Returns the raw JSON value of [decimals]. + * + * Unlike [decimals], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("decimals") @ExcludeMissing fun _decimals(): JsonField = decimals + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [symbol]. + * + * Unlike [symbol], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("symbol") @ExcludeMissing fun _symbol(): JsonField = symbol + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Currency]. */ + fun builder() = Builder() + } + + /** A builder for [Currency]. */ + class Builder internal constructor() { + + private var code: JsonField = JsonMissing.of() + private var decimals: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var symbol: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(currency: Currency) = apply { + code = currency.code + decimals = currency.decimals + name = currency.name + symbol = currency.symbol + additionalProperties = currency.additionalProperties.toMutableMap() + } + + /** + * Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use + * their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) + */ + fun code(code: String) = code(JsonField.of(code)) + + /** + * Sets [Builder.code] to an arbitrary JSON value. + * + * You should usually call [Builder.code] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun code(code: JsonField) = apply { this.code = code } + + /** Number of decimal places for the currency */ + fun decimals(decimals: Long) = decimals(JsonField.of(decimals)) + + /** + * Sets [Builder.decimals] to an arbitrary JSON value. + * + * You should usually call [Builder.decimals] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun decimals(decimals: JsonField) = apply { this.decimals = decimals } + + /** Full name of the currency */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** Symbol of the currency */ + fun symbol(symbol: String) = symbol(JsonField.of(symbol)) + + /** + * Sets [Builder.symbol] to an arbitrary JSON value. + * + * You should usually call [Builder.symbol] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun symbol(symbol: JsonField) = apply { this.symbol = symbol } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Currency]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Currency = + Currency(code, decimals, name, symbol, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Currency = apply { + if (validated) { + return@apply + } + + code() + decimals() + name() + symbol() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (code.asKnown() == null) 0 else 1) + + (if (decimals.asKnown() == null) 0 else 1) + + (if (name.asKnown() == null) 0 else 1) + + (if (symbol.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Currency && + code == other.code && + decimals == other.decimals && + name == other.name && + symbol == other.symbol && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(code, decimals, name, symbol, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Currency{code=$code, decimals=$decimals, name=$name, symbol=$symbol, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/OutgoingRateDetails.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/OutgoingRateDetails.kt new file mode 100644 index 00000000..2c927623 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/OutgoingRateDetails.kt @@ -0,0 +1,450 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** Details about the rate and fees for an outgoing transaction or quote. */ +class OutgoingRateDetails +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val counterpartyFixedFee: JsonField, + private val counterpartyMultiplier: JsonField, + private val gridApiFixedFee: JsonField, + private val gridApiMultiplier: JsonField, + private val gridApiVariableFeeAmount: JsonField, + private val gridApiVariableFeeRate: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("counterpartyFixedFee") + @ExcludeMissing + counterpartyFixedFee: JsonField = JsonMissing.of(), + @JsonProperty("counterpartyMultiplier") + @ExcludeMissing + counterpartyMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("gridApiFixedFee") + @ExcludeMissing + gridApiFixedFee: JsonField = JsonMissing.of(), + @JsonProperty("gridApiMultiplier") + @ExcludeMissing + gridApiMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("gridApiVariableFeeAmount") + @ExcludeMissing + gridApiVariableFeeAmount: JsonField = JsonMissing.of(), + @JsonProperty("gridApiVariableFeeRate") + @ExcludeMissing + gridApiVariableFeeRate: JsonField = JsonMissing.of(), + ) : this( + counterpartyFixedFee, + counterpartyMultiplier, + gridApiFixedFee, + gridApiMultiplier, + gridApiVariableFeeAmount, + gridApiVariableFeeRate, + mutableMapOf(), + ) + + /** + * The fixed fee charged by the counterparty institution to execute the quote in the smallest + * unit of the receiving currency (eg. cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun counterpartyFixedFee(): Long = counterpartyFixedFee.getRequired("counterpartyFixedFee") + + /** + * The underlying multiplier from mSATs to the receiving currency as returned by the + * counterparty institution. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun counterpartyMultiplier(): Double = + counterpartyMultiplier.getRequired("counterpartyMultiplier") + + /** + * The fixed fee charged by the Grid product to execute the quote in the smallest unit of the + * sending currency (eg. cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiFixedFee(): Long = gridApiFixedFee.getRequired("gridApiFixedFee") + + /** + * The underlying multiplier from the sending currency to mSATS, including variable fees. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiMultiplier(): Double = gridApiMultiplier.getRequired("gridApiMultiplier") + + /** + * The variable fee amount charged by the Grid product to execute the quote in the smallest unit + * of the sending currency (eg. cents). This is the sending amount times gridApiVariableFeeRate. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiVariableFeeAmount(): Double = + gridApiVariableFeeAmount.getRequired("gridApiVariableFeeAmount") + + /** + * The variable fee rate charged by the Grid product to execute the quote as a percentage of the + * sending currency amount. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiVariableFeeRate(): Double = + gridApiVariableFeeRate.getRequired("gridApiVariableFeeRate") + + /** + * Returns the raw JSON value of [counterpartyFixedFee]. + * + * Unlike [counterpartyFixedFee], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("counterpartyFixedFee") + @ExcludeMissing + fun _counterpartyFixedFee(): JsonField = counterpartyFixedFee + + /** + * Returns the raw JSON value of [counterpartyMultiplier]. + * + * Unlike [counterpartyMultiplier], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("counterpartyMultiplier") + @ExcludeMissing + fun _counterpartyMultiplier(): JsonField = counterpartyMultiplier + + /** + * Returns the raw JSON value of [gridApiFixedFee]. + * + * Unlike [gridApiFixedFee], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("gridApiFixedFee") + @ExcludeMissing + fun _gridApiFixedFee(): JsonField = gridApiFixedFee + + /** + * Returns the raw JSON value of [gridApiMultiplier]. + * + * Unlike [gridApiMultiplier], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("gridApiMultiplier") + @ExcludeMissing + fun _gridApiMultiplier(): JsonField = gridApiMultiplier + + /** + * Returns the raw JSON value of [gridApiVariableFeeAmount]. + * + * Unlike [gridApiVariableFeeAmount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("gridApiVariableFeeAmount") + @ExcludeMissing + fun _gridApiVariableFeeAmount(): JsonField = gridApiVariableFeeAmount + + /** + * Returns the raw JSON value of [gridApiVariableFeeRate]. + * + * Unlike [gridApiVariableFeeRate], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("gridApiVariableFeeRate") + @ExcludeMissing + fun _gridApiVariableFeeRate(): JsonField = gridApiVariableFeeRate + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [OutgoingRateDetails]. + * + * The following fields are required: + * ```kotlin + * .counterpartyFixedFee() + * .counterpartyMultiplier() + * .gridApiFixedFee() + * .gridApiMultiplier() + * .gridApiVariableFeeAmount() + * .gridApiVariableFeeRate() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [OutgoingRateDetails]. */ + class Builder internal constructor() { + + private var counterpartyFixedFee: JsonField? = null + private var counterpartyMultiplier: JsonField? = null + private var gridApiFixedFee: JsonField? = null + private var gridApiMultiplier: JsonField? = null + private var gridApiVariableFeeAmount: JsonField? = null + private var gridApiVariableFeeRate: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(outgoingRateDetails: OutgoingRateDetails) = apply { + counterpartyFixedFee = outgoingRateDetails.counterpartyFixedFee + counterpartyMultiplier = outgoingRateDetails.counterpartyMultiplier + gridApiFixedFee = outgoingRateDetails.gridApiFixedFee + gridApiMultiplier = outgoingRateDetails.gridApiMultiplier + gridApiVariableFeeAmount = outgoingRateDetails.gridApiVariableFeeAmount + gridApiVariableFeeRate = outgoingRateDetails.gridApiVariableFeeRate + additionalProperties = outgoingRateDetails.additionalProperties.toMutableMap() + } + + /** + * The fixed fee charged by the counterparty institution to execute the quote in the + * smallest unit of the receiving currency (eg. cents). + */ + fun counterpartyFixedFee(counterpartyFixedFee: Long) = + counterpartyFixedFee(JsonField.of(counterpartyFixedFee)) + + /** + * Sets [Builder.counterpartyFixedFee] to an arbitrary JSON value. + * + * You should usually call [Builder.counterpartyFixedFee] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun counterpartyFixedFee(counterpartyFixedFee: JsonField) = apply { + this.counterpartyFixedFee = counterpartyFixedFee + } + + /** + * The underlying multiplier from mSATs to the receiving currency as returned by the + * counterparty institution. + */ + fun counterpartyMultiplier(counterpartyMultiplier: Double) = + counterpartyMultiplier(JsonField.of(counterpartyMultiplier)) + + /** + * Sets [Builder.counterpartyMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.counterpartyMultiplier] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun counterpartyMultiplier(counterpartyMultiplier: JsonField) = apply { + this.counterpartyMultiplier = counterpartyMultiplier + } + + /** + * The fixed fee charged by the Grid product to execute the quote in the smallest unit of + * the sending currency (eg. cents). + */ + fun gridApiFixedFee(gridApiFixedFee: Long) = gridApiFixedFee(JsonField.of(gridApiFixedFee)) + + /** + * Sets [Builder.gridApiFixedFee] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiFixedFee] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun gridApiFixedFee(gridApiFixedFee: JsonField) = apply { + this.gridApiFixedFee = gridApiFixedFee + } + + /** + * The underlying multiplier from the sending currency to mSATS, including variable fees. + */ + fun gridApiMultiplier(gridApiMultiplier: Double) = + gridApiMultiplier(JsonField.of(gridApiMultiplier)) + + /** + * Sets [Builder.gridApiMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiMultiplier] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gridApiMultiplier(gridApiMultiplier: JsonField) = apply { + this.gridApiMultiplier = gridApiMultiplier + } + + /** + * The variable fee amount charged by the Grid product to execute the quote in the smallest + * unit of the sending currency (eg. cents). This is the sending amount times + * gridApiVariableFeeRate. + */ + fun gridApiVariableFeeAmount(gridApiVariableFeeAmount: Double) = + gridApiVariableFeeAmount(JsonField.of(gridApiVariableFeeAmount)) + + /** + * Sets [Builder.gridApiVariableFeeAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiVariableFeeAmount] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun gridApiVariableFeeAmount(gridApiVariableFeeAmount: JsonField) = apply { + this.gridApiVariableFeeAmount = gridApiVariableFeeAmount + } + + /** + * The variable fee rate charged by the Grid product to execute the quote as a percentage of + * the sending currency amount. + */ + fun gridApiVariableFeeRate(gridApiVariableFeeRate: Double) = + gridApiVariableFeeRate(JsonField.of(gridApiVariableFeeRate)) + + /** + * Sets [Builder.gridApiVariableFeeRate] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiVariableFeeRate] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gridApiVariableFeeRate(gridApiVariableFeeRate: JsonField) = apply { + this.gridApiVariableFeeRate = gridApiVariableFeeRate + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [OutgoingRateDetails]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .counterpartyFixedFee() + * .counterpartyMultiplier() + * .gridApiFixedFee() + * .gridApiMultiplier() + * .gridApiVariableFeeAmount() + * .gridApiVariableFeeRate() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutgoingRateDetails = + OutgoingRateDetails( + checkRequired("counterpartyFixedFee", counterpartyFixedFee), + checkRequired("counterpartyMultiplier", counterpartyMultiplier), + checkRequired("gridApiFixedFee", gridApiFixedFee), + checkRequired("gridApiMultiplier", gridApiMultiplier), + checkRequired("gridApiVariableFeeAmount", gridApiVariableFeeAmount), + checkRequired("gridApiVariableFeeRate", gridApiVariableFeeRate), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutgoingRateDetails = apply { + if (validated) { + return@apply + } + + counterpartyFixedFee() + counterpartyMultiplier() + gridApiFixedFee() + gridApiMultiplier() + gridApiVariableFeeAmount() + gridApiVariableFeeRate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (counterpartyFixedFee.asKnown() == null) 0 else 1) + + (if (counterpartyMultiplier.asKnown() == null) 0 else 1) + + (if (gridApiFixedFee.asKnown() == null) 0 else 1) + + (if (gridApiMultiplier.asKnown() == null) 0 else 1) + + (if (gridApiVariableFeeAmount.asKnown() == null) 0 else 1) + + (if (gridApiVariableFeeRate.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutgoingRateDetails && + counterpartyFixedFee == other.counterpartyFixedFee && + counterpartyMultiplier == other.counterpartyMultiplier && + gridApiFixedFee == other.gridApiFixedFee && + gridApiMultiplier == other.gridApiMultiplier && + gridApiVariableFeeAmount == other.gridApiVariableFeeAmount && + gridApiVariableFeeRate == other.gridApiVariableFeeRate && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + counterpartyFixedFee, + counterpartyMultiplier, + gridApiFixedFee, + gridApiMultiplier, + gridApiVariableFeeAmount, + gridApiVariableFeeRate, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutgoingRateDetails{counterpartyFixedFee=$counterpartyFixedFee, counterpartyMultiplier=$counterpartyMultiplier, gridApiFixedFee=$gridApiFixedFee, gridApiMultiplier=$gridApiMultiplier, gridApiVariableFeeAmount=$gridApiVariableFeeAmount, gridApiVariableFeeRate=$gridApiVariableFeeRate, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/PaymentInstructions.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/PaymentInstructions.kt new file mode 100644 index 00000000..f1e3b55f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/PaymentInstructions.kt @@ -0,0 +1,6145 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class PaymentInstructions +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val accountOrWalletInfo: JsonField, + private val instructionsNotes: JsonField, + private val isPlatformAccount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("accountOrWalletInfo") + @ExcludeMissing + accountOrWalletInfo: JsonField = JsonMissing.of(), + @JsonProperty("instructionsNotes") + @ExcludeMissing + instructionsNotes: JsonField = JsonMissing.of(), + @JsonProperty("isPlatformAccount") + @ExcludeMissing + isPlatformAccount: JsonField = JsonMissing.of(), + ) : this(accountOrWalletInfo, instructionsNotes, isPlatformAccount, mutableMapOf()) + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountOrWalletInfo(): AccountOrWalletInfo = + accountOrWalletInfo.getRequired("accountOrWalletInfo") + + /** + * Additional human-readable instructions for making the payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun instructionsNotes(): String? = instructionsNotes.getNullable("instructionsNotes") + + /** + * Indicates whether the account is a platform account or a customer account. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun isPlatformAccount(): Boolean? = isPlatformAccount.getNullable("isPlatformAccount") + + /** + * Returns the raw JSON value of [accountOrWalletInfo]. + * + * Unlike [accountOrWalletInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountOrWalletInfo") + @ExcludeMissing + fun _accountOrWalletInfo(): JsonField = accountOrWalletInfo + + /** + * Returns the raw JSON value of [instructionsNotes]. + * + * Unlike [instructionsNotes], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("instructionsNotes") + @ExcludeMissing + fun _instructionsNotes(): JsonField = instructionsNotes + + /** + * Returns the raw JSON value of [isPlatformAccount]. + * + * Unlike [isPlatformAccount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("isPlatformAccount") + @ExcludeMissing + fun _isPlatformAccount(): JsonField = isPlatformAccount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PaymentInstructions]. + * + * The following fields are required: + * ```kotlin + * .accountOrWalletInfo() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PaymentInstructions]. */ + class Builder internal constructor() { + + private var accountOrWalletInfo: JsonField? = null + private var instructionsNotes: JsonField = JsonMissing.of() + private var isPlatformAccount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(paymentInstructions: PaymentInstructions) = apply { + accountOrWalletInfo = paymentInstructions.accountOrWalletInfo + instructionsNotes = paymentInstructions.instructionsNotes + isPlatformAccount = paymentInstructions.isPlatformAccount + additionalProperties = paymentInstructions.additionalProperties.toMutableMap() + } + + fun accountOrWalletInfo(accountOrWalletInfo: AccountOrWalletInfo) = + accountOrWalletInfo(JsonField.of(accountOrWalletInfo)) + + /** + * Sets [Builder.accountOrWalletInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.accountOrWalletInfo] with a well-typed + * [AccountOrWalletInfo] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun accountOrWalletInfo(accountOrWalletInfo: JsonField) = apply { + this.accountOrWalletInfo = accountOrWalletInfo + } + + /** Alias for calling [accountOrWalletInfo] with `AccountOrWalletInfo.ofClabe(clabe)`. */ + fun accountOrWalletInfo(clabe: AccountOrWalletInfo.Clabe) = + accountOrWalletInfo(AccountOrWalletInfo.ofClabe(clabe)) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofUsAccount(usAccount)`. + */ + fun accountOrWalletInfo(usAccount: AccountOrWalletInfo.UsAccount) = + accountOrWalletInfo(AccountOrWalletInfo.ofUsAccount(usAccount)) + + /** Alias for calling [accountOrWalletInfo] with `AccountOrWalletInfo.ofPix(pix)`. */ + fun accountOrWalletInfo(pix: AccountOrWalletInfo.Pix) = + accountOrWalletInfo(AccountOrWalletInfo.ofPix(pix)) + + /** Alias for calling [accountOrWalletInfo] with `AccountOrWalletInfo.ofIban(iban)`. */ + fun accountOrWalletInfo(iban: AccountOrWalletInfo.Iban) = + accountOrWalletInfo(AccountOrWalletInfo.ofIban(iban)) + + /** Alias for calling [accountOrWalletInfo] with `AccountOrWalletInfo.ofUpi(upi)`. */ + fun accountOrWalletInfo(upi: AccountOrWalletInfo.Upi) = + accountOrWalletInfo(AccountOrWalletInfo.ofUpi(upi)) + + /** + * Alias for calling [accountOrWalletInfo] with the following: + * ```kotlin + * AccountOrWalletInfo.Upi.builder() + * .accountType(PaymentInstructions.AccountOrWalletInfo.Upi.AccountType.UPI) + * .vpa(vpa) + * .build() + * ``` + */ + fun upiAccountOrWalletInfo(vpa: String) = + accountOrWalletInfo( + AccountOrWalletInfo.Upi.builder() + .accountType(PaymentInstructions.AccountOrWalletInfo.Upi.AccountType.UPI) + .vpa(vpa) + .build() + ) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofSparkWallet(sparkWallet)`. + */ + fun accountOrWalletInfo(sparkWallet: AccountOrWalletInfo.SparkWallet) = + accountOrWalletInfo(AccountOrWalletInfo.ofSparkWallet(sparkWallet)) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofLightning(lightning)`. + */ + fun accountOrWalletInfo(lightning: AccountOrWalletInfo.Lightning) = + accountOrWalletInfo(AccountOrWalletInfo.ofLightning(lightning)) + + /** + * Alias for calling [accountOrWalletInfo] with the following: + * ```kotlin + * AccountOrWalletInfo.Lightning.builder() + * .accountType(PaymentInstructions.AccountOrWalletInfo.Lightning.AccountType.LIGHTNING) + * .invoice(invoice) + * .build() + * ``` + */ + fun lightningAccountOrWalletInfo(invoice: String) = + accountOrWalletInfo( + AccountOrWalletInfo.Lightning.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Lightning.AccountType.LIGHTNING + ) + .invoice(invoice) + .build() + ) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofSolanaWallet(solanaWallet)`. + */ + fun accountOrWalletInfo(solanaWallet: AccountOrWalletInfo.SolanaWallet) = + accountOrWalletInfo(AccountOrWalletInfo.ofSolanaWallet(solanaWallet)) + + /** + * Alias for calling [accountOrWalletInfo] with the following: + * ```kotlin + * AccountOrWalletInfo.SolanaWallet.builder() + * .accountType(PaymentInstructions.AccountOrWalletInfo.SolanaWallet.AccountType.SOLANA_WALLET) + * .address(address) + * .build() + * ``` + */ + fun solanaWalletAccountOrWalletInfo(address: String) = + accountOrWalletInfo( + AccountOrWalletInfo.SolanaWallet.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.SolanaWallet.AccountType + .SOLANA_WALLET + ) + .address(address) + .build() + ) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofTronWallet(tronWallet)`. + */ + fun accountOrWalletInfo(tronWallet: AccountOrWalletInfo.TronWallet) = + accountOrWalletInfo(AccountOrWalletInfo.ofTronWallet(tronWallet)) + + /** + * Alias for calling [accountOrWalletInfo] with the following: + * ```kotlin + * AccountOrWalletInfo.TronWallet.builder() + * .accountType(PaymentInstructions.AccountOrWalletInfo.TronWallet.AccountType.TRON_WALLET) + * .address(address) + * .build() + * ``` + */ + fun tronWalletAccountOrWalletInfo(address: String) = + accountOrWalletInfo( + AccountOrWalletInfo.TronWallet.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.TronWallet.AccountType.TRON_WALLET + ) + .address(address) + .build() + ) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofPolygonWallet(polygonWallet)`. + */ + fun accountOrWalletInfo(polygonWallet: AccountOrWalletInfo.PolygonWallet) = + accountOrWalletInfo(AccountOrWalletInfo.ofPolygonWallet(polygonWallet)) + + /** + * Alias for calling [accountOrWalletInfo] with the following: + * ```kotlin + * AccountOrWalletInfo.PolygonWallet.builder() + * .accountType(PaymentInstructions.AccountOrWalletInfo.PolygonWallet.AccountType.POLYGON_WALLET) + * .address(address) + * .build() + * ``` + */ + fun polygonWalletAccountOrWalletInfo(address: String) = + accountOrWalletInfo( + AccountOrWalletInfo.PolygonWallet.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.PolygonWallet.AccountType + .POLYGON_WALLET + ) + .address(address) + .build() + ) + + /** + * Alias for calling [accountOrWalletInfo] with + * `AccountOrWalletInfo.ofBaseWallet(baseWallet)`. + */ + fun accountOrWalletInfo(baseWallet: AccountOrWalletInfo.BaseWallet) = + accountOrWalletInfo(AccountOrWalletInfo.ofBaseWallet(baseWallet)) + + /** + * Alias for calling [accountOrWalletInfo] with the following: + * ```kotlin + * AccountOrWalletInfo.BaseWallet.builder() + * .accountType(PaymentInstructions.AccountOrWalletInfo.BaseWallet.AccountType.BASE_WALLET) + * .address(address) + * .build() + * ``` + */ + fun baseWalletAccountOrWalletInfo(address: String) = + accountOrWalletInfo( + AccountOrWalletInfo.BaseWallet.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.BaseWallet.AccountType.BASE_WALLET + ) + .address(address) + .build() + ) + + /** Additional human-readable instructions for making the payment */ + fun instructionsNotes(instructionsNotes: String) = + instructionsNotes(JsonField.of(instructionsNotes)) + + /** + * Sets [Builder.instructionsNotes] to an arbitrary JSON value. + * + * You should usually call [Builder.instructionsNotes] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun instructionsNotes(instructionsNotes: JsonField) = apply { + this.instructionsNotes = instructionsNotes + } + + /** Indicates whether the account is a platform account or a customer account. */ + fun isPlatformAccount(isPlatformAccount: Boolean) = + isPlatformAccount(JsonField.of(isPlatformAccount)) + + /** + * Sets [Builder.isPlatformAccount] to an arbitrary JSON value. + * + * You should usually call [Builder.isPlatformAccount] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun isPlatformAccount(isPlatformAccount: JsonField) = apply { + this.isPlatformAccount = isPlatformAccount + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PaymentInstructions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountOrWalletInfo() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PaymentInstructions = + PaymentInstructions( + checkRequired("accountOrWalletInfo", accountOrWalletInfo), + instructionsNotes, + isPlatformAccount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PaymentInstructions = apply { + if (validated) { + return@apply + } + + accountOrWalletInfo().validate() + instructionsNotes() + isPlatformAccount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountOrWalletInfo.asKnown()?.validity() ?: 0) + + (if (instructionsNotes.asKnown() == null) 0 else 1) + + (if (isPlatformAccount.asKnown() == null) 0 else 1) + + @JsonDeserialize(using = AccountOrWalletInfo.Deserializer::class) + @JsonSerialize(using = AccountOrWalletInfo.Serializer::class) + class AccountOrWalletInfo + private constructor( + private val clabe: Clabe? = null, + private val usAccount: UsAccount? = null, + private val pix: Pix? = null, + private val iban: Iban? = null, + private val upi: Upi? = null, + private val sparkWallet: SparkWallet? = null, + private val lightning: Lightning? = null, + private val solanaWallet: SolanaWallet? = null, + private val tronWallet: TronWallet? = null, + private val polygonWallet: PolygonWallet? = null, + private val baseWallet: BaseWallet? = null, + private val _json: JsonValue? = null, + ) { + + fun clabe(): Clabe? = clabe + + fun usAccount(): UsAccount? = usAccount + + fun pix(): Pix? = pix + + fun iban(): Iban? = iban + + fun upi(): Upi? = upi + + fun sparkWallet(): SparkWallet? = sparkWallet + + fun lightning(): Lightning? = lightning + + fun solanaWallet(): SolanaWallet? = solanaWallet + + fun tronWallet(): TronWallet? = tronWallet + + fun polygonWallet(): PolygonWallet? = polygonWallet + + fun baseWallet(): BaseWallet? = baseWallet + + fun isClabe(): Boolean = clabe != null + + fun isUsAccount(): Boolean = usAccount != null + + fun isPix(): Boolean = pix != null + + fun isIban(): Boolean = iban != null + + fun isUpi(): Boolean = upi != null + + fun isSparkWallet(): Boolean = sparkWallet != null + + fun isLightning(): Boolean = lightning != null + + fun isSolanaWallet(): Boolean = solanaWallet != null + + fun isTronWallet(): Boolean = tronWallet != null + + fun isPolygonWallet(): Boolean = polygonWallet != null + + fun isBaseWallet(): Boolean = baseWallet != null + + fun asClabe(): Clabe = clabe.getOrThrow("clabe") + + fun asUsAccount(): UsAccount = usAccount.getOrThrow("usAccount") + + fun asPix(): Pix = pix.getOrThrow("pix") + + fun asIban(): Iban = iban.getOrThrow("iban") + + fun asUpi(): Upi = upi.getOrThrow("upi") + + fun asSparkWallet(): SparkWallet = sparkWallet.getOrThrow("sparkWallet") + + fun asLightning(): Lightning = lightning.getOrThrow("lightning") + + fun asSolanaWallet(): SolanaWallet = solanaWallet.getOrThrow("solanaWallet") + + fun asTronWallet(): TronWallet = tronWallet.getOrThrow("tronWallet") + + fun asPolygonWallet(): PolygonWallet = polygonWallet.getOrThrow("polygonWallet") + + fun asBaseWallet(): BaseWallet = baseWallet.getOrThrow("baseWallet") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + clabe != null -> visitor.visitClabe(clabe) + usAccount != null -> visitor.visitUsAccount(usAccount) + pix != null -> visitor.visitPix(pix) + iban != null -> visitor.visitIban(iban) + upi != null -> visitor.visitUpi(upi) + sparkWallet != null -> visitor.visitSparkWallet(sparkWallet) + lightning != null -> visitor.visitLightning(lightning) + solanaWallet != null -> visitor.visitSolanaWallet(solanaWallet) + tronWallet != null -> visitor.visitTronWallet(tronWallet) + polygonWallet != null -> visitor.visitPolygonWallet(polygonWallet) + baseWallet != null -> visitor.visitBaseWallet(baseWallet) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): AccountOrWalletInfo = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitClabe(clabe: Clabe) { + clabe.validate() + } + + override fun visitUsAccount(usAccount: UsAccount) { + usAccount.validate() + } + + override fun visitPix(pix: Pix) { + pix.validate() + } + + override fun visitIban(iban: Iban) { + iban.validate() + } + + override fun visitUpi(upi: Upi) { + upi.validate() + } + + override fun visitSparkWallet(sparkWallet: SparkWallet) { + sparkWallet.validate() + } + + override fun visitLightning(lightning: Lightning) { + lightning.validate() + } + + override fun visitSolanaWallet(solanaWallet: SolanaWallet) { + solanaWallet.validate() + } + + override fun visitTronWallet(tronWallet: TronWallet) { + tronWallet.validate() + } + + override fun visitPolygonWallet(polygonWallet: PolygonWallet) { + polygonWallet.validate() + } + + override fun visitBaseWallet(baseWallet: BaseWallet) { + baseWallet.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitClabe(clabe: Clabe) = clabe.validity() + + override fun visitUsAccount(usAccount: UsAccount) = usAccount.validity() + + override fun visitPix(pix: Pix) = pix.validity() + + override fun visitIban(iban: Iban) = iban.validity() + + override fun visitUpi(upi: Upi) = upi.validity() + + override fun visitSparkWallet(sparkWallet: SparkWallet) = sparkWallet.validity() + + override fun visitLightning(lightning: Lightning) = lightning.validity() + + override fun visitSolanaWallet(solanaWallet: SolanaWallet) = + solanaWallet.validity() + + override fun visitTronWallet(tronWallet: TronWallet) = tronWallet.validity() + + override fun visitPolygonWallet(polygonWallet: PolygonWallet) = + polygonWallet.validity() + + override fun visitBaseWallet(baseWallet: BaseWallet) = baseWallet.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountOrWalletInfo && + clabe == other.clabe && + usAccount == other.usAccount && + pix == other.pix && + iban == other.iban && + upi == other.upi && + sparkWallet == other.sparkWallet && + lightning == other.lightning && + solanaWallet == other.solanaWallet && + tronWallet == other.tronWallet && + polygonWallet == other.polygonWallet && + baseWallet == other.baseWallet + } + + override fun hashCode(): Int = + Objects.hash( + clabe, + usAccount, + pix, + iban, + upi, + sparkWallet, + lightning, + solanaWallet, + tronWallet, + polygonWallet, + baseWallet, + ) + + override fun toString(): String = + when { + clabe != null -> "AccountOrWalletInfo{clabe=$clabe}" + usAccount != null -> "AccountOrWalletInfo{usAccount=$usAccount}" + pix != null -> "AccountOrWalletInfo{pix=$pix}" + iban != null -> "AccountOrWalletInfo{iban=$iban}" + upi != null -> "AccountOrWalletInfo{upi=$upi}" + sparkWallet != null -> "AccountOrWalletInfo{sparkWallet=$sparkWallet}" + lightning != null -> "AccountOrWalletInfo{lightning=$lightning}" + solanaWallet != null -> "AccountOrWalletInfo{solanaWallet=$solanaWallet}" + tronWallet != null -> "AccountOrWalletInfo{tronWallet=$tronWallet}" + polygonWallet != null -> "AccountOrWalletInfo{polygonWallet=$polygonWallet}" + baseWallet != null -> "AccountOrWalletInfo{baseWallet=$baseWallet}" + _json != null -> "AccountOrWalletInfo{_unknown=$_json}" + else -> throw IllegalStateException("Invalid AccountOrWalletInfo") + } + + companion object { + + fun ofClabe(clabe: Clabe) = AccountOrWalletInfo(clabe = clabe) + + fun ofUsAccount(usAccount: UsAccount) = AccountOrWalletInfo(usAccount = usAccount) + + fun ofPix(pix: Pix) = AccountOrWalletInfo(pix = pix) + + fun ofIban(iban: Iban) = AccountOrWalletInfo(iban = iban) + + fun ofUpi(upi: Upi) = AccountOrWalletInfo(upi = upi) + + fun ofSparkWallet(sparkWallet: SparkWallet) = + AccountOrWalletInfo(sparkWallet = sparkWallet) + + fun ofLightning(lightning: Lightning) = AccountOrWalletInfo(lightning = lightning) + + fun ofSolanaWallet(solanaWallet: SolanaWallet) = + AccountOrWalletInfo(solanaWallet = solanaWallet) + + fun ofTronWallet(tronWallet: TronWallet) = AccountOrWalletInfo(tronWallet = tronWallet) + + fun ofPolygonWallet(polygonWallet: PolygonWallet) = + AccountOrWalletInfo(polygonWallet = polygonWallet) + + fun ofBaseWallet(baseWallet: BaseWallet) = AccountOrWalletInfo(baseWallet = baseWallet) + } + + /** + * An interface that defines how to map each variant of [AccountOrWalletInfo] to a value of + * type [T]. + */ + interface Visitor { + + fun visitClabe(clabe: Clabe): T + + fun visitUsAccount(usAccount: UsAccount): T + + fun visitPix(pix: Pix): T + + fun visitIban(iban: Iban): T + + fun visitUpi(upi: Upi): T + + fun visitSparkWallet(sparkWallet: SparkWallet): T + + fun visitLightning(lightning: Lightning): T + + fun visitSolanaWallet(solanaWallet: SolanaWallet): T + + fun visitTronWallet(tronWallet: TronWallet): T + + fun visitPolygonWallet(polygonWallet: PolygonWallet): T + + fun visitBaseWallet(baseWallet: BaseWallet): T + + /** + * Maps an unknown variant of [AccountOrWalletInfo] to a value of type [T]. + * + * An instance of [AccountOrWalletInfo] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK + * is on an older version than the API, then the API may respond with new variants that + * the SDK is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown AccountOrWalletInfo: $json") + } + } + + internal class Deserializer : + BaseDeserializer(AccountOrWalletInfo::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): AccountOrWalletInfo { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(clabe = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(usAccount = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(pix = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(iban = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(upi = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(sparkWallet = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(lightning = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(solanaWallet = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(tronWallet = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(polygonWallet = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + AccountOrWalletInfo(baseWallet = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> AccountOrWalletInfo(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(AccountOrWalletInfo::class) { + + override fun serialize( + value: AccountOrWalletInfo, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.clabe != null -> generator.writeObject(value.clabe) + value.usAccount != null -> generator.writeObject(value.usAccount) + value.pix != null -> generator.writeObject(value.pix) + value.iban != null -> generator.writeObject(value.iban) + value.upi != null -> generator.writeObject(value.upi) + value.sparkWallet != null -> generator.writeObject(value.sparkWallet) + value.lightning != null -> generator.writeObject(value.lightning) + value.solanaWallet != null -> generator.writeObject(value.solanaWallet) + value.tronWallet != null -> generator.writeObject(value.tronWallet) + value.polygonWallet != null -> generator.writeObject(value.polygonWallet) + value.baseWallet != null -> generator.writeObject(value.baseWallet) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid AccountOrWalletInfo") + } + } + } + + class Clabe + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val clabeNumber: JsonField, + private val reference: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("clabeNumber") + @ExcludeMissing + clabeNumber: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + ) : this(accountType, clabeNumber, reference, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * 18-digit CLABE number (Mexican banking standard) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun clabeNumber(): String = clabeNumber.getRequired("clabeNumber") + + /** + * Unique reference code that must be included with the payment to properly credit it + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [clabeNumber]. + * + * Unlike [clabeNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("clabeNumber") + @ExcludeMissing + fun _clabeNumber(): JsonField = clabeNumber + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Clabe]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .clabeNumber() + * .reference() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Clabe]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var clabeNumber: JsonField? = null + private var reference: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(clabe: Clabe) = apply { + accountType = clabe.accountType + clabeNumber = clabe.clabeNumber + reference = clabe.reference + additionalProperties = clabe.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** 18-digit CLABE number (Mexican banking standard) */ + fun clabeNumber(clabeNumber: String) = clabeNumber(JsonField.of(clabeNumber)) + + /** + * Sets [Builder.clabeNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.clabeNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun clabeNumber(clabeNumber: JsonField) = apply { + this.clabeNumber = clabeNumber + } + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Clabe]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .clabeNumber() + * .reference() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Clabe = + Clabe( + checkRequired("accountType", accountType), + checkRequired("clabeNumber", clabeNumber), + checkRequired("reference", reference), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Clabe = apply { + if (validated) { + return@apply + } + + accountType().validate() + clabeNumber() + reference() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (clabeNumber.asKnown() == null) 0 else 1) + + (if (reference.asKnown() == null) 0 else 1) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val CLABE = of("CLABE") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + CLABE + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CLABE, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CLABE -> Value.CLABE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CLABE -> Known.CLABE + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Clabe && + accountType == other.accountType && + clabeNumber == other.clabeNumber && + reference == other.reference && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, clabeNumber, reference, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Clabe{accountType=$accountType, clabeNumber=$clabeNumber, reference=$reference, additionalProperties=$additionalProperties}" + } + + class UsAccount + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountCategory: JsonField, + private val accountNumber: JsonField, + private val accountType: JsonField, + private val reference: JsonField, + private val routingNumber: JsonField, + private val bankName: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountCategory") + @ExcludeMissing + accountCategory: JsonField = JsonMissing.of(), + @JsonProperty("accountNumber") + @ExcludeMissing + accountNumber: JsonField = JsonMissing.of(), + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + @JsonProperty("routingNumber") + @ExcludeMissing + routingNumber: JsonField = JsonMissing.of(), + @JsonProperty("bankName") + @ExcludeMissing + bankName: JsonField = JsonMissing.of(), + ) : this( + accountCategory, + accountNumber, + accountType, + reference, + routingNumber, + bankName, + mutableMapOf(), + ) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * Type of account (checking or savings) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountCategory(): AccountCategory = accountCategory.getRequired("accountCategory") + + /** + * US bank account number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountNumber(): String = accountNumber.getRequired("accountNumber") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Unique reference code that must be included with the payment to properly credit it + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * ACH routing number (9 digits) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun routingNumber(): String = routingNumber.getRequired("routingNumber") + + /** + * Name of the bank + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun bankName(): String? = bankName.getNullable("bankName") + + /** + * Returns the raw JSON value of [accountCategory]. + * + * Unlike [accountCategory], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("accountCategory") + @ExcludeMissing + fun _accountCategory(): JsonField = accountCategory + + /** + * Returns the raw JSON value of [accountNumber]. + * + * Unlike [accountNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountNumber") + @ExcludeMissing + fun _accountNumber(): JsonField = accountNumber + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference + + /** + * Returns the raw JSON value of [routingNumber]. + * + * Unlike [routingNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("routingNumber") + @ExcludeMissing + fun _routingNumber(): JsonField = routingNumber + + /** + * Returns the raw JSON value of [bankName]. + * + * Unlike [bankName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("bankName") @ExcludeMissing fun _bankName(): JsonField = bankName + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UsAccount]. + * + * The following fields are required: + * ```kotlin + * .accountCategory() + * .accountNumber() + * .accountType() + * .reference() + * .routingNumber() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UsAccount]. */ + class Builder internal constructor() { + + private var accountCategory: JsonField? = null + private var accountNumber: JsonField? = null + private var accountType: JsonField? = null + private var reference: JsonField? = null + private var routingNumber: JsonField? = null + private var bankName: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(usAccount: UsAccount) = apply { + accountCategory = usAccount.accountCategory + accountNumber = usAccount.accountNumber + accountType = usAccount.accountType + reference = usAccount.reference + routingNumber = usAccount.routingNumber + bankName = usAccount.bankName + additionalProperties = usAccount.additionalProperties.toMutableMap() + } + + /** Type of account (checking or savings) */ + fun accountCategory(accountCategory: AccountCategory) = + accountCategory(JsonField.of(accountCategory)) + + /** + * Sets [Builder.accountCategory] to an arbitrary JSON value. + * + * You should usually call [Builder.accountCategory] with a well-typed + * [AccountCategory] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun accountCategory(accountCategory: JsonField) = apply { + this.accountCategory = accountCategory + } + + /** US bank account number */ + fun accountNumber(accountNumber: String) = + accountNumber(JsonField.of(accountNumber)) + + /** + * Sets [Builder.accountNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.accountNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun accountNumber(accountNumber: JsonField) = apply { + this.accountNumber = accountNumber + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + /** ACH routing number (9 digits) */ + fun routingNumber(routingNumber: String) = + routingNumber(JsonField.of(routingNumber)) + + /** + * Sets [Builder.routingNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.routingNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun routingNumber(routingNumber: JsonField) = apply { + this.routingNumber = routingNumber + } + + /** Name of the bank */ + fun bankName(bankName: String) = bankName(JsonField.of(bankName)) + + /** + * Sets [Builder.bankName] to an arbitrary JSON value. + * + * You should usually call [Builder.bankName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun bankName(bankName: JsonField) = apply { this.bankName = bankName } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UsAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountCategory() + * .accountNumber() + * .accountType() + * .reference() + * .routingNumber() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UsAccount = + UsAccount( + checkRequired("accountCategory", accountCategory), + checkRequired("accountNumber", accountNumber), + checkRequired("accountType", accountType), + checkRequired("reference", reference), + checkRequired("routingNumber", routingNumber), + bankName, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UsAccount = apply { + if (validated) { + return@apply + } + + accountCategory().validate() + accountNumber() + accountType().validate() + reference() + routingNumber() + bankName() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountCategory.asKnown()?.validity() ?: 0) + + (if (accountNumber.asKnown() == null) 0 else 1) + + (accountType.asKnown()?.validity() ?: 0) + + (if (reference.asKnown() == null) 0 else 1) + + (if (routingNumber.asKnown() == null) 0 else 1) + + (if (bankName.asKnown() == null) 0 else 1) + + /** Type of account (checking or savings) */ + class AccountCategory + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val CHECKING = of("CHECKING") + + val SAVINGS = of("SAVINGS") + + fun of(value: String) = AccountCategory(JsonField.of(value)) + } + + /** An enum containing [AccountCategory]'s known values. */ + enum class Known { + CHECKING, + SAVINGS, + } + + /** + * An enum containing [AccountCategory]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [AccountCategory] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CHECKING, + SAVINGS, + /** + * An enum member indicating that [AccountCategory] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CHECKING -> Value.CHECKING + SAVINGS -> Value.SAVINGS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CHECKING -> Known.CHECKING + SAVINGS -> Known.SAVINGS + else -> throw GridInvalidDataException("Unknown AccountCategory: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountCategory = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountCategory && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val US_ACCOUNT = of("US_ACCOUNT") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + US_ACCOUNT + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + US_ACCOUNT, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + US_ACCOUNT -> Value.US_ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + US_ACCOUNT -> Known.US_ACCOUNT + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UsAccount && + accountCategory == other.accountCategory && + accountNumber == other.accountNumber && + accountType == other.accountType && + reference == other.reference && + routingNumber == other.routingNumber && + bankName == other.bankName && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountCategory, + accountNumber, + accountType, + reference, + routingNumber, + bankName, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UsAccount{accountCategory=$accountCategory, accountNumber=$accountNumber, accountType=$accountType, reference=$reference, routingNumber=$routingNumber, bankName=$bankName, additionalProperties=$additionalProperties}" + } + + class Pix + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val pixKey: JsonField, + private val pixKeyType: JsonField, + private val taxId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("pixKey") + @ExcludeMissing + pixKey: JsonField = JsonMissing.of(), + @JsonProperty("pixKeyType") + @ExcludeMissing + pixKeyType: JsonField = JsonMissing.of(), + @JsonProperty("taxId") @ExcludeMissing taxId: JsonField = JsonMissing.of(), + ) : this(accountType, pixKey, pixKeyType, taxId, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * PIX key for Brazilian instant payments + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pixKey(): String = pixKey.getRequired("pixKey") + + /** + * Type of PIX key being used + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun pixKeyType(): PixKeyType = pixKeyType.getRequired("pixKeyType") + + /** + * Tax ID of the account holder + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun taxId(): String = taxId.getRequired("taxId") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [pixKey]. + * + * Unlike [pixKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pixKey") @ExcludeMissing fun _pixKey(): JsonField = pixKey + + /** + * Returns the raw JSON value of [pixKeyType]. + * + * Unlike [pixKeyType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("pixKeyType") + @ExcludeMissing + fun _pixKeyType(): JsonField = pixKeyType + + /** + * Returns the raw JSON value of [taxId]. + * + * Unlike [taxId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("taxId") @ExcludeMissing fun _taxId(): JsonField = taxId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Pix]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .pixKey() + * .pixKeyType() + * .taxId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Pix]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var pixKey: JsonField? = null + private var pixKeyType: JsonField? = null + private var taxId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(pix: Pix) = apply { + accountType = pix.accountType + pixKey = pix.pixKey + pixKeyType = pix.pixKeyType + taxId = pix.taxId + additionalProperties = pix.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** PIX key for Brazilian instant payments */ + fun pixKey(pixKey: String) = pixKey(JsonField.of(pixKey)) + + /** + * Sets [Builder.pixKey] to an arbitrary JSON value. + * + * You should usually call [Builder.pixKey] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pixKey(pixKey: JsonField) = apply { this.pixKey = pixKey } + + /** Type of PIX key being used */ + fun pixKeyType(pixKeyType: PixKeyType) = pixKeyType(JsonField.of(pixKeyType)) + + /** + * Sets [Builder.pixKeyType] to an arbitrary JSON value. + * + * You should usually call [Builder.pixKeyType] with a well-typed [PixKeyType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pixKeyType(pixKeyType: JsonField) = apply { + this.pixKeyType = pixKeyType + } + + /** Tax ID of the account holder */ + fun taxId(taxId: String) = taxId(JsonField.of(taxId)) + + /** + * Sets [Builder.taxId] to an arbitrary JSON value. + * + * You should usually call [Builder.taxId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun taxId(taxId: JsonField) = apply { this.taxId = taxId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Pix]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .pixKey() + * .pixKeyType() + * .taxId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Pix = + Pix( + checkRequired("accountType", accountType), + checkRequired("pixKey", pixKey), + checkRequired("pixKeyType", pixKeyType), + checkRequired("taxId", taxId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Pix = apply { + if (validated) { + return@apply + } + + accountType().validate() + pixKey() + pixKeyType().validate() + taxId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (pixKey.asKnown() == null) 0 else 1) + + (pixKeyType.asKnown()?.validity() ?: 0) + + (if (taxId.asKnown() == null) 0 else 1) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PIX = of("PIX") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + PIX + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PIX, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PIX -> Value.PIX + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + PIX -> Known.PIX + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Type of PIX key being used */ + class PixKeyType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val CPF = of("CPF") + + val CNPJ = of("CNPJ") + + val EMAIL = of("EMAIL") + + val PHONE = of("PHONE") + + val RANDOM = of("RANDOM") + + fun of(value: String) = PixKeyType(JsonField.of(value)) + } + + /** An enum containing [PixKeyType]'s known values. */ + enum class Known { + CPF, + CNPJ, + EMAIL, + PHONE, + RANDOM, + } + + /** + * An enum containing [PixKeyType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [PixKeyType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CPF, + CNPJ, + EMAIL, + PHONE, + RANDOM, + /** + * An enum member indicating that [PixKeyType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + CPF -> Value.CPF + CNPJ -> Value.CNPJ + EMAIL -> Value.EMAIL + PHONE -> Value.PHONE + RANDOM -> Value.RANDOM + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + CPF -> Known.CPF + CNPJ -> Known.CNPJ + EMAIL -> Known.EMAIL + PHONE -> Known.PHONE + RANDOM -> Known.RANDOM + else -> throw GridInvalidDataException("Unknown PixKeyType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): PixKeyType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PixKeyType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Pix && + accountType == other.accountType && + pixKey == other.pixKey && + pixKeyType == other.pixKeyType && + taxId == other.taxId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, pixKey, pixKeyType, taxId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Pix{accountType=$accountType, pixKey=$pixKey, pixKeyType=$pixKeyType, taxId=$taxId, additionalProperties=$additionalProperties}" + } + + class Iban + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val iban: JsonField, + private val reference: JsonField, + private val swiftBic: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("iban") @ExcludeMissing iban: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + @JsonProperty("swiftBic") + @ExcludeMissing + swiftBic: JsonField = JsonMissing.of(), + ) : this(accountType, iban, reference, swiftBic, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * International Bank Account Number + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun iban(): String = iban.getRequired("iban") + + /** + * Unique reference code that must be included with the payment to properly credit it + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * SWIFT/BIC code (8 or 11 characters) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun swiftBic(): String = swiftBic.getRequired("swiftBic") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [iban]. + * + * Unlike [iban], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("iban") @ExcludeMissing fun _iban(): JsonField = iban + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reference") + @ExcludeMissing + fun _reference(): JsonField = reference + + /** + * Returns the raw JSON value of [swiftBic]. + * + * Unlike [swiftBic], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("swiftBic") @ExcludeMissing fun _swiftBic(): JsonField = swiftBic + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Iban]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .iban() + * .reference() + * .swiftBic() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Iban]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var iban: JsonField? = null + private var reference: JsonField? = null + private var swiftBic: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(iban: Iban) = apply { + accountType = iban.accountType + this.iban = iban.iban + reference = iban.reference + swiftBic = iban.swiftBic + additionalProperties = iban.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** International Bank Account Number */ + fun iban(iban: String) = iban(JsonField.of(iban)) + + /** + * Sets [Builder.iban] to an arbitrary JSON value. + * + * You should usually call [Builder.iban] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun iban(iban: JsonField) = apply { this.iban = iban } + + /** + * Unique reference code that must be included with the payment to properly credit + * it + */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + /** SWIFT/BIC code (8 or 11 characters) */ + fun swiftBic(swiftBic: String) = swiftBic(JsonField.of(swiftBic)) + + /** + * Sets [Builder.swiftBic] to an arbitrary JSON value. + * + * You should usually call [Builder.swiftBic] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun swiftBic(swiftBic: JsonField) = apply { this.swiftBic = swiftBic } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Iban]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .iban() + * .reference() + * .swiftBic() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Iban = + Iban( + checkRequired("accountType", accountType), + checkRequired("iban", iban), + checkRequired("reference", reference), + checkRequired("swiftBic", swiftBic), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Iban = apply { + if (validated) { + return@apply + } + + accountType().validate() + iban() + reference() + swiftBic() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (iban.asKnown() == null) 0 else 1) + + (if (reference.asKnown() == null) 0 else 1) + + (if (swiftBic.asKnown() == null) 0 else 1) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val IBAN = of("IBAN") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + IBAN + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IBAN, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IBAN -> Value.IBAN + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + IBAN -> Known.IBAN + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Iban && + accountType == other.accountType && + iban == other.iban && + reference == other.reference && + swiftBic == other.swiftBic && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, iban, reference, swiftBic, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Iban{accountType=$accountType, iban=$iban, reference=$reference, swiftBic=$swiftBic, additionalProperties=$additionalProperties}" + } + + class Upi + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val vpa: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("vpa") @ExcludeMissing vpa: JsonField = JsonMissing.of(), + ) : this(accountType, vpa, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Virtual Payment Address for UPI payments + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun vpa(): String = vpa.getRequired("vpa") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [vpa]. + * + * Unlike [vpa], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("vpa") @ExcludeMissing fun _vpa(): JsonField = vpa + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Upi]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .vpa() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Upi]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var vpa: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(upi: Upi) = apply { + accountType = upi.accountType + vpa = upi.vpa + additionalProperties = upi.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** Virtual Payment Address for UPI payments */ + fun vpa(vpa: String) = vpa(JsonField.of(vpa)) + + /** + * Sets [Builder.vpa] to an arbitrary JSON value. + * + * You should usually call [Builder.vpa] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun vpa(vpa: JsonField) = apply { this.vpa = vpa } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Upi]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .vpa() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Upi = + Upi( + checkRequired("accountType", accountType), + checkRequired("vpa", vpa), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Upi = apply { + if (validated) { + return@apply + } + + accountType().validate() + vpa() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + (if (vpa.asKnown() == null) 0 else 1) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val UPI = of("UPI") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + UPI + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + UPI, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + UPI -> Value.UPI + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + UPI -> Known.UPI + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Upi && + accountType == other.accountType && + vpa == other.vpa && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, vpa, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Upi{accountType=$accountType, vpa=$vpa, additionalProperties=$additionalProperties}" + } + + class SparkWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val address: JsonField, + private val assetType: JsonField, + private val invoice: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField = JsonMissing.of(), + @JsonProperty("assetType") + @ExcludeMissing + assetType: JsonField = JsonMissing.of(), + @JsonProperty("invoice") + @ExcludeMissing + invoice: JsonField = JsonMissing.of(), + ) : this(accountType, address, assetType, invoice, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Spark wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun address(): String = address.getRequired("address") + + /** + * Type of asset + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun assetType(): AssetType = assetType.getRequired("assetType") + + /** + * Invoice for the payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun invoice(): String? = invoice.getNullable("invoice") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [assetType]. + * + * Unlike [assetType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("assetType") + @ExcludeMissing + fun _assetType(): JsonField = assetType + + /** + * Returns the raw JSON value of [invoice]. + * + * Unlike [invoice], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("invoice") @ExcludeMissing fun _invoice(): JsonField = invoice + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SparkWallet]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * .assetType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SparkWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var address: JsonField? = null + private var assetType: JsonField? = null + private var invoice: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(sparkWallet: SparkWallet) = apply { + accountType = sparkWallet.accountType + address = sparkWallet.address + assetType = sparkWallet.assetType + invoice = sparkWallet.invoice + additionalProperties = sparkWallet.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** Spark wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** Type of asset */ + fun assetType(assetType: AssetType) = assetType(JsonField.of(assetType)) + + /** + * Sets [Builder.assetType] to an arbitrary JSON value. + * + * You should usually call [Builder.assetType] with a well-typed [AssetType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun assetType(assetType: JsonField) = apply { + this.assetType = assetType + } + + /** Invoice for the payment */ + fun invoice(invoice: String) = invoice(JsonField.of(invoice)) + + /** + * Sets [Builder.invoice] to an arbitrary JSON value. + * + * You should usually call [Builder.invoice] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun invoice(invoice: JsonField) = apply { this.invoice = invoice } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SparkWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * .assetType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SparkWallet = + SparkWallet( + checkRequired("accountType", accountType), + checkRequired("address", address), + checkRequired("assetType", assetType), + invoice, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SparkWallet = apply { + if (validated) { + return@apply + } + + accountType().validate() + address() + assetType().validate() + invoice() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (address.asKnown() == null) 0 else 1) + + (assetType.asKnown()?.validity() ?: 0) + + (if (invoice.asKnown() == null) 0 else 1) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val SPARK_WALLET = of("SPARK_WALLET") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + SPARK_WALLET + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + SPARK_WALLET, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + SPARK_WALLET -> Value.SPARK_WALLET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + SPARK_WALLET -> Known.SPARK_WALLET + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Type of asset */ + class AssetType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BTC = of("BTC") + + val USDB = of("USDB") + + fun of(value: String) = AssetType(JsonField.of(value)) + } + + /** An enum containing [AssetType]'s known values. */ + enum class Known { + BTC, + USDB, + } + + /** + * An enum containing [AssetType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AssetType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BTC, + USDB, + /** + * An enum member indicating that [AssetType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BTC -> Value.BTC + USDB -> Value.USDB + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BTC -> Known.BTC + USDB -> Known.USDB + else -> throw GridInvalidDataException("Unknown AssetType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AssetType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AssetType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SparkWallet && + accountType == other.accountType && + address == other.address && + assetType == other.assetType && + invoice == other.invoice && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, assetType, invoice, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SparkWallet{accountType=$accountType, address=$address, assetType=$assetType, invoice=$invoice, additionalProperties=$additionalProperties}" + } + + class Lightning + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val invoice: JsonField, + private val accountType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("invoice") + @ExcludeMissing + invoice: JsonField = JsonMissing.of(), + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + ) : this(invoice, accountType, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * Invoice for the payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun invoice(): String = invoice.getRequired("invoice") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun accountType(): AccountType? = accountType.getNullable("accountType") + + /** + * Returns the raw JSON value of [invoice]. + * + * Unlike [invoice], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("invoice") @ExcludeMissing fun _invoice(): JsonField = invoice + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Lightning]. + * + * The following fields are required: + * ```kotlin + * .invoice() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Lightning]. */ + class Builder internal constructor() { + + private var invoice: JsonField? = null + private var accountType: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(lightning: Lightning) = apply { + invoice = lightning.invoice + accountType = lightning.accountType + additionalProperties = lightning.additionalProperties.toMutableMap() + } + + /** Invoice for the payment */ + fun invoice(invoice: String) = invoice(JsonField.of(invoice)) + + /** + * Sets [Builder.invoice] to an arbitrary JSON value. + * + * You should usually call [Builder.invoice] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun invoice(invoice: JsonField) = apply { this.invoice = invoice } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Lightning]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .invoice() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Lightning = + Lightning( + checkRequired("invoice", invoice), + accountType, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Lightning = apply { + if (validated) { + return@apply + } + + invoice() + accountType()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (invoice.asKnown() == null) 0 else 1) + (accountType.asKnown()?.validity() ?: 0) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val LIGHTNING = of("LIGHTNING") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + LIGHTNING + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LIGHTNING, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LIGHTNING -> Value.LIGHTNING + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + LIGHTNING -> Known.LIGHTNING + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Lightning && + invoice == other.invoice && + accountType == other.accountType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(invoice, accountType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Lightning{invoice=$invoice, accountType=$accountType, additionalProperties=$additionalProperties}" + } + + class SolanaWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val address: JsonField, + private val assetType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField = JsonMissing.of(), + @JsonProperty("assetType") + @ExcludeMissing + assetType: JsonField = JsonMissing.of(), + ) : this(accountType, address, assetType, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Solana wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun address(): String = address.getRequired("address") + + /** + * Type of asset + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun assetType(): AssetType? = assetType.getNullable("assetType") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [assetType]. + * + * Unlike [assetType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("assetType") + @ExcludeMissing + fun _assetType(): JsonField = assetType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SolanaWallet]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SolanaWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var address: JsonField? = null + private var assetType: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(solanaWallet: SolanaWallet) = apply { + accountType = solanaWallet.accountType + address = solanaWallet.address + assetType = solanaWallet.assetType + additionalProperties = solanaWallet.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** Solana wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** Type of asset */ + fun assetType(assetType: AssetType) = assetType(JsonField.of(assetType)) + + /** + * Sets [Builder.assetType] to an arbitrary JSON value. + * + * You should usually call [Builder.assetType] with a well-typed [AssetType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun assetType(assetType: JsonField) = apply { + this.assetType = assetType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SolanaWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SolanaWallet = + SolanaWallet( + checkRequired("accountType", accountType), + checkRequired("address", address), + assetType, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SolanaWallet = apply { + if (validated) { + return@apply + } + + accountType().validate() + address() + assetType()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (address.asKnown() == null) 0 else 1) + + (assetType.asKnown()?.validity() ?: 0) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val SOLANA_WALLET = of("SOLANA_WALLET") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + SOLANA_WALLET + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + SOLANA_WALLET, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + SOLANA_WALLET -> Value.SOLANA_WALLET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + SOLANA_WALLET -> Known.SOLANA_WALLET + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Type of asset */ + class AssetType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val USDC = of("USDC") + + val USDT = of("USDT") + + fun of(value: String) = AssetType(JsonField.of(value)) + } + + /** An enum containing [AssetType]'s known values. */ + enum class Known { + USDC, + USDT, + } + + /** + * An enum containing [AssetType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AssetType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + USDC, + USDT, + /** + * An enum member indicating that [AssetType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + USDC -> Value.USDC + USDT -> Value.USDT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + USDC -> Known.USDC + USDT -> Known.USDT + else -> throw GridInvalidDataException("Unknown AssetType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AssetType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AssetType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SolanaWallet && + accountType == other.accountType && + address == other.address && + assetType == other.assetType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, assetType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SolanaWallet{accountType=$accountType, address=$address, assetType=$assetType, additionalProperties=$additionalProperties}" + } + + class TronWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val address: JsonField, + private val assetType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField = JsonMissing.of(), + @JsonProperty("assetType") + @ExcludeMissing + assetType: JsonField = JsonMissing.of(), + ) : this(accountType, address, assetType, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Tron wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun address(): String = address.getRequired("address") + + /** + * Type of asset + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun assetType(): AssetType? = assetType.getNullable("assetType") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [assetType]. + * + * Unlike [assetType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("assetType") + @ExcludeMissing + fun _assetType(): JsonField = assetType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TronWallet]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TronWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var address: JsonField? = null + private var assetType: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(tronWallet: TronWallet) = apply { + accountType = tronWallet.accountType + address = tronWallet.address + assetType = tronWallet.assetType + additionalProperties = tronWallet.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** Tron wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** Type of asset */ + fun assetType(assetType: AssetType) = assetType(JsonField.of(assetType)) + + /** + * Sets [Builder.assetType] to an arbitrary JSON value. + * + * You should usually call [Builder.assetType] with a well-typed [AssetType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun assetType(assetType: JsonField) = apply { + this.assetType = assetType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [TronWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TronWallet = + TronWallet( + checkRequired("accountType", accountType), + checkRequired("address", address), + assetType, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): TronWallet = apply { + if (validated) { + return@apply + } + + accountType().validate() + address() + assetType()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (address.asKnown() == null) 0 else 1) + + (assetType.asKnown()?.validity() ?: 0) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val TRON_WALLET = of("TRON_WALLET") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + TRON_WALLET + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + TRON_WALLET, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + TRON_WALLET -> Value.TRON_WALLET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + TRON_WALLET -> Known.TRON_WALLET + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Type of asset */ + class AssetType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val USDT = of("USDT") + + fun of(value: String) = AssetType(JsonField.of(value)) + } + + /** An enum containing [AssetType]'s known values. */ + enum class Known { + USDT + } + + /** + * An enum containing [AssetType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AssetType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + USDT, + /** + * An enum member indicating that [AssetType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + USDT -> Value.USDT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + USDT -> Known.USDT + else -> throw GridInvalidDataException("Unknown AssetType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AssetType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AssetType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TronWallet && + accountType == other.accountType && + address == other.address && + assetType == other.assetType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, assetType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TronWallet{accountType=$accountType, address=$address, assetType=$assetType, additionalProperties=$additionalProperties}" + } + + class PolygonWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val address: JsonField, + private val assetType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField = JsonMissing.of(), + @JsonProperty("assetType") + @ExcludeMissing + assetType: JsonField = JsonMissing.of(), + ) : this(accountType, address, assetType, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Polygon eth wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun address(): String = address.getRequired("address") + + /** + * Type of asset + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun assetType(): AssetType? = assetType.getNullable("assetType") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [assetType]. + * + * Unlike [assetType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("assetType") + @ExcludeMissing + fun _assetType(): JsonField = assetType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PolygonWallet]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [PolygonWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var address: JsonField? = null + private var assetType: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(polygonWallet: PolygonWallet) = apply { + accountType = polygonWallet.accountType + address = polygonWallet.address + assetType = polygonWallet.assetType + additionalProperties = polygonWallet.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** Polygon eth wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** Type of asset */ + fun assetType(assetType: AssetType) = assetType(JsonField.of(assetType)) + + /** + * Sets [Builder.assetType] to an arbitrary JSON value. + * + * You should usually call [Builder.assetType] with a well-typed [AssetType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun assetType(assetType: JsonField) = apply { + this.assetType = assetType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [PolygonWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PolygonWallet = + PolygonWallet( + checkRequired("accountType", accountType), + checkRequired("address", address), + assetType, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): PolygonWallet = apply { + if (validated) { + return@apply + } + + accountType().validate() + address() + assetType()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (address.asKnown() == null) 0 else 1) + + (assetType.asKnown()?.validity() ?: 0) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val POLYGON_WALLET = of("POLYGON_WALLET") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + POLYGON_WALLET + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + POLYGON_WALLET, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + POLYGON_WALLET -> Value.POLYGON_WALLET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + POLYGON_WALLET -> Known.POLYGON_WALLET + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Type of asset */ + class AssetType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val USDC = of("USDC") + + fun of(value: String) = AssetType(JsonField.of(value)) + } + + /** An enum containing [AssetType]'s known values. */ + enum class Known { + USDC + } + + /** + * An enum containing [AssetType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AssetType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + USDC, + /** + * An enum member indicating that [AssetType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + USDC -> Value.USDC + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + USDC -> Known.USDC + else -> throw GridInvalidDataException("Unknown AssetType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AssetType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AssetType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PolygonWallet && + accountType == other.accountType && + address == other.address && + assetType == other.assetType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, assetType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PolygonWallet{accountType=$accountType, address=$address, assetType=$assetType, additionalProperties=$additionalProperties}" + } + + class BaseWallet + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountType: JsonField, + private val address: JsonField, + private val assetType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountType") + @ExcludeMissing + accountType: JsonField = JsonMissing.of(), + @JsonProperty("address") + @ExcludeMissing + address: JsonField = JsonMissing.of(), + @JsonProperty("assetType") + @ExcludeMissing + assetType: JsonField = JsonMissing.of(), + ) : this(accountType, address, assetType, mutableMapOf()) + + fun toBasePaymentAccountInfo(): BasePaymentAccountInfo = + BasePaymentAccountInfo.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountType(): AccountType = accountType.getRequired("accountType") + + /** + * Base eth wallet address + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun address(): String = address.getRequired("address") + + /** + * Type of asset + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun assetType(): AssetType? = assetType.getNullable("assetType") + + /** + * Returns the raw JSON value of [accountType]. + * + * Unlike [accountType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountType") + @ExcludeMissing + fun _accountType(): JsonField = accountType + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [assetType]. + * + * Unlike [assetType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("assetType") + @ExcludeMissing + fun _assetType(): JsonField = assetType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BaseWallet]. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [BaseWallet]. */ + class Builder internal constructor() { + + private var accountType: JsonField? = null + private var address: JsonField? = null + private var assetType: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(baseWallet: BaseWallet) = apply { + accountType = baseWallet.accountType + address = baseWallet.address + assetType = baseWallet.assetType + additionalProperties = baseWallet.additionalProperties.toMutableMap() + } + + fun accountType(accountType: AccountType) = accountType(JsonField.of(accountType)) + + /** + * Sets [Builder.accountType] to an arbitrary JSON value. + * + * You should usually call [Builder.accountType] with a well-typed [AccountType] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun accountType(accountType: JsonField) = apply { + this.accountType = accountType + } + + /** Base eth wallet address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** Type of asset */ + fun assetType(assetType: AssetType) = assetType(JsonField.of(assetType)) + + /** + * Sets [Builder.assetType] to an arbitrary JSON value. + * + * You should usually call [Builder.assetType] with a well-typed [AssetType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun assetType(assetType: JsonField) = apply { + this.assetType = assetType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BaseWallet]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountType() + * .address() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BaseWallet = + BaseWallet( + checkRequired("accountType", accountType), + checkRequired("address", address), + assetType, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BaseWallet = apply { + if (validated) { + return@apply + } + + accountType().validate() + address() + assetType()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (accountType.asKnown()?.validity() ?: 0) + + (if (address.asKnown() == null) 0 else 1) + + (assetType.asKnown()?.validity() ?: 0) + + class AccountType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val BASE_WALLET = of("BASE_WALLET") + + fun of(value: String) = AccountType(JsonField.of(value)) + } + + /** An enum containing [AccountType]'s known values. */ + enum class Known { + BASE_WALLET + } + + /** + * An enum containing [AccountType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AccountType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + BASE_WALLET, + /** + * An enum member indicating that [AccountType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + BASE_WALLET -> Value.BASE_WALLET + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + BASE_WALLET -> Known.BASE_WALLET + else -> throw GridInvalidDataException("Unknown AccountType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AccountType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccountType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Type of asset */ + class AssetType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val USDC = of("USDC") + + fun of(value: String) = AssetType(JsonField.of(value)) + } + + /** An enum containing [AssetType]'s known values. */ + enum class Known { + USDC + } + + /** + * An enum containing [AssetType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AssetType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + USDC, + /** + * An enum member indicating that [AssetType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + USDC -> Value.USDC + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + USDC -> Known.USDC + else -> throw GridInvalidDataException("Unknown AssetType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): AssetType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AssetType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BaseWallet && + accountType == other.accountType && + address == other.address && + assetType == other.assetType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountType, address, assetType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BaseWallet{accountType=$accountType, address=$address, assetType=$assetType, additionalProperties=$additionalProperties}" + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is PaymentInstructions && + accountOrWalletInfo == other.accountOrWalletInfo && + instructionsNotes == other.instructionsNotes && + isPlatformAccount == other.isPlatformAccount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + accountOrWalletInfo, + instructionsNotes, + isPlatformAccount, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "PaymentInstructions{accountOrWalletInfo=$accountOrWalletInfo, instructionsNotes=$instructionsNotes, isPlatformAccount=$isPlatformAccount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Quote.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Quote.kt new file mode 100644 index 00000000..39e7d66f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/Quote.kt @@ -0,0 +1,1132 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class Quote +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val createdAt: JsonField, + private val destination: JsonField, + private val exchangeRate: JsonField, + private val expiresAt: JsonField, + private val feesIncluded: JsonField, + private val quoteId: JsonField, + private val receivingCurrency: JsonField, + private val sendingCurrency: JsonField, + private val source: JsonField, + private val status: JsonField, + private val totalReceivingAmount: JsonField, + private val totalSendingAmount: JsonField, + private val transactionId: JsonField, + private val originalQuoteId: JsonField, + private val paymentInstructions: JsonField>, + private val rateDetails: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("exchangeRate") + @ExcludeMissing + exchangeRate: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("feesIncluded") + @ExcludeMissing + feesIncluded: JsonField = JsonMissing.of(), + @JsonProperty("quoteId") @ExcludeMissing quoteId: JsonField = JsonMissing.of(), + @JsonProperty("receivingCurrency") + @ExcludeMissing + receivingCurrency: JsonField = JsonMissing.of(), + @JsonProperty("sendingCurrency") + @ExcludeMissing + sendingCurrency: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("totalReceivingAmount") + @ExcludeMissing + totalReceivingAmount: JsonField = JsonMissing.of(), + @JsonProperty("totalSendingAmount") + @ExcludeMissing + totalSendingAmount: JsonField = JsonMissing.of(), + @JsonProperty("transactionId") + @ExcludeMissing + transactionId: JsonField = JsonMissing.of(), + @JsonProperty("originalQuoteId") + @ExcludeMissing + originalQuoteId: JsonField = JsonMissing.of(), + @JsonProperty("paymentInstructions") + @ExcludeMissing + paymentInstructions: JsonField> = JsonMissing.of(), + @JsonProperty("rateDetails") + @ExcludeMissing + rateDetails: JsonField = JsonMissing.of(), + ) : this( + createdAt, + destination, + exchangeRate, + expiresAt, + feesIncluded, + quoteId, + receivingCurrency, + sendingCurrency, + source, + status, + totalReceivingAmount, + totalSendingAmount, + transactionId, + originalQuoteId, + paymentInstructions, + rateDetails, + mutableMapOf(), + ) + + /** + * When this quote was created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime = createdAt.getRequired("createdAt") + + /** + * Destination account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): QuoteDestinationOneOf = destination.getRequired("destination") + + /** + * Number of sending currency units per receiving currency unit. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun exchangeRate(): Double = exchangeRate.getRequired("exchangeRate") + + /** + * When this quote expires (typically 1-5 minutes after creation) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun expiresAt(): OffsetDateTime = expiresAt.getRequired("expiresAt") + + /** + * The fees associated with the quote in the smallest unit of the sending currency (eg. cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun feesIncluded(): Long = feesIncluded.getRequired("feesIncluded") + + /** + * Unique identifier for this quote + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun quoteId(): String = quoteId.getRequired("quoteId") + + /** + * Currency for the receiving amount + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivingCurrency(): Currency = receivingCurrency.getRequired("receivingCurrency") + + /** + * Currency for the sending amount + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun sendingCurrency(): Currency = sendingCurrency.getRequired("sendingCurrency") + + /** + * Source account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): QuoteSourceOneOf = source.getRequired("source") + + /** + * Current status of the quote + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): Status = status.getRequired("status") + + /** + * The total amount that will be received in the smallest unit of the receiving currency (eg. + * cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun totalReceivingAmount(): Long = totalReceivingAmount.getRequired("totalReceivingAmount") + + /** + * The total amount that will be sent in the smallest unit of the sending currency (eg. cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun totalSendingAmount(): Long = totalSendingAmount.getRequired("totalSendingAmount") + + /** + * The ID of the transaction created from this quote. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun transactionId(): String = transactionId.getRequired("transactionId") + + /** + * ID of the quote that is being retried + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun originalQuoteId(): String? = originalQuoteId.getNullable("originalQuoteId") + + /** + * Payment instructions for executing the payment. This is not required when using an internal + * account source. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun paymentInstructions(): List? = + paymentInstructions.getNullable("paymentInstructions") + + /** + * Details about the rate and fees for the transaction. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun rateDetails(): OutgoingRateDetails? = rateDetails.getNullable("rateDetails") + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [exchangeRate]. + * + * Unlike [exchangeRate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("exchangeRate") + @ExcludeMissing + fun _exchangeRate(): JsonField = exchangeRate + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [feesIncluded]. + * + * Unlike [feesIncluded], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("feesIncluded") + @ExcludeMissing + fun _feesIncluded(): JsonField = feesIncluded + + /** + * Returns the raw JSON value of [quoteId]. + * + * Unlike [quoteId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("quoteId") @ExcludeMissing fun _quoteId(): JsonField = quoteId + + /** + * Returns the raw JSON value of [receivingCurrency]. + * + * Unlike [receivingCurrency], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("receivingCurrency") + @ExcludeMissing + fun _receivingCurrency(): JsonField = receivingCurrency + + /** + * Returns the raw JSON value of [sendingCurrency]. + * + * Unlike [sendingCurrency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sendingCurrency") + @ExcludeMissing + fun _sendingCurrency(): JsonField = sendingCurrency + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [totalReceivingAmount]. + * + * Unlike [totalReceivingAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("totalReceivingAmount") + @ExcludeMissing + fun _totalReceivingAmount(): JsonField = totalReceivingAmount + + /** + * Returns the raw JSON value of [totalSendingAmount]. + * + * Unlike [totalSendingAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("totalSendingAmount") + @ExcludeMissing + fun _totalSendingAmount(): JsonField = totalSendingAmount + + /** + * Returns the raw JSON value of [transactionId]. + * + * Unlike [transactionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("transactionId") + @ExcludeMissing + fun _transactionId(): JsonField = transactionId + + /** + * Returns the raw JSON value of [originalQuoteId]. + * + * Unlike [originalQuoteId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("originalQuoteId") + @ExcludeMissing + fun _originalQuoteId(): JsonField = originalQuoteId + + /** + * Returns the raw JSON value of [paymentInstructions]. + * + * Unlike [paymentInstructions], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("paymentInstructions") + @ExcludeMissing + fun _paymentInstructions(): JsonField> = paymentInstructions + + /** + * Returns the raw JSON value of [rateDetails]. + * + * Unlike [rateDetails], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("rateDetails") + @ExcludeMissing + fun _rateDetails(): JsonField = rateDetails + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Quote]. + * + * The following fields are required: + * ```kotlin + * .createdAt() + * .destination() + * .exchangeRate() + * .expiresAt() + * .feesIncluded() + * .quoteId() + * .receivingCurrency() + * .sendingCurrency() + * .source() + * .status() + * .totalReceivingAmount() + * .totalSendingAmount() + * .transactionId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Quote]. */ + class Builder internal constructor() { + + private var createdAt: JsonField? = null + private var destination: JsonField? = null + private var exchangeRate: JsonField? = null + private var expiresAt: JsonField? = null + private var feesIncluded: JsonField? = null + private var quoteId: JsonField? = null + private var receivingCurrency: JsonField? = null + private var sendingCurrency: JsonField? = null + private var source: JsonField? = null + private var status: JsonField? = null + private var totalReceivingAmount: JsonField? = null + private var totalSendingAmount: JsonField? = null + private var transactionId: JsonField? = null + private var originalQuoteId: JsonField = JsonMissing.of() + private var paymentInstructions: JsonField>? = null + private var rateDetails: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(quote: Quote) = apply { + createdAt = quote.createdAt + destination = quote.destination + exchangeRate = quote.exchangeRate + expiresAt = quote.expiresAt + feesIncluded = quote.feesIncluded + quoteId = quote.quoteId + receivingCurrency = quote.receivingCurrency + sendingCurrency = quote.sendingCurrency + source = quote.source + status = quote.status + totalReceivingAmount = quote.totalReceivingAmount + totalSendingAmount = quote.totalSendingAmount + transactionId = quote.transactionId + originalQuoteId = quote.originalQuoteId + paymentInstructions = quote.paymentInstructions.map { it.toMutableList() } + rateDetails = quote.rateDetails + additionalProperties = quote.additionalProperties.toMutableMap() + } + + /** When this quote was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Destination account details */ + fun destination(destination: QuoteDestinationOneOf) = destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [QuoteDestinationOneOf] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Alias for calling [destination] with `QuoteDestinationOneOf.ofAccount(account)`. */ + fun destination(account: QuoteDestinationOneOf.Account) = + destination(QuoteDestinationOneOf.ofAccount(account)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.Account.builder() + * .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountDestination(accountId: String) = + destination( + QuoteDestinationOneOf.Account.builder() + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** + * Alias for calling [destination] with `QuoteDestinationOneOf.ofUmaAddress(umaAddress)`. + */ + fun destination(umaAddress: QuoteDestinationOneOf.UmaAddress) = + destination(QuoteDestinationOneOf.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.UmaAddress.builder() + * .destinationType(QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressDestination(umaAddress: String) = + destination( + QuoteDestinationOneOf.UmaAddress.builder() + .destinationType(QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS) + .umaAddress(umaAddress) + .build() + ) + + /** + * Alias for calling [destination] with + * `QuoteDestinationOneOf.ofExternalAccountDetails(externalAccountDetails)`. + */ + fun destination(externalAccountDetails: QuoteDestinationOneOf.ExternalAccountDetails) = + destination(QuoteDestinationOneOf.ofExternalAccountDetails(externalAccountDetails)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.ExternalAccountDetails.builder() + * .destinationType(QuoteDestinationOneOf.ExternalAccountDetails.DestinationType.EXTERNAL_ACCOUNT_DETAILS) + * .externalAccountDetails(externalAccountDetails) + * .build() + * ``` + */ + fun externalAccountDetailsDestination(externalAccountDetails: ExternalAccountCreate) = + destination( + QuoteDestinationOneOf.ExternalAccountDetails.builder() + .destinationType( + QuoteDestinationOneOf.ExternalAccountDetails.DestinationType + .EXTERNAL_ACCOUNT_DETAILS + ) + .externalAccountDetails(externalAccountDetails) + .build() + ) + + /** Number of sending currency units per receiving currency unit. */ + fun exchangeRate(exchangeRate: Double) = exchangeRate(JsonField.of(exchangeRate)) + + /** + * Sets [Builder.exchangeRate] to an arbitrary JSON value. + * + * You should usually call [Builder.exchangeRate] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun exchangeRate(exchangeRate: JsonField) = apply { + this.exchangeRate = exchangeRate + } + + /** When this quote expires (typically 1-5 minutes after creation) */ + fun expiresAt(expiresAt: OffsetDateTime) = expiresAt(JsonField.of(expiresAt)) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** + * The fees associated with the quote in the smallest unit of the sending currency (eg. + * cents). + */ + fun feesIncluded(feesIncluded: Long) = feesIncluded(JsonField.of(feesIncluded)) + + /** + * Sets [Builder.feesIncluded] to an arbitrary JSON value. + * + * You should usually call [Builder.feesIncluded] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun feesIncluded(feesIncluded: JsonField) = apply { this.feesIncluded = feesIncluded } + + /** Unique identifier for this quote */ + fun quoteId(quoteId: String) = quoteId(JsonField.of(quoteId)) + + /** + * Sets [Builder.quoteId] to an arbitrary JSON value. + * + * You should usually call [Builder.quoteId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun quoteId(quoteId: JsonField) = apply { this.quoteId = quoteId } + + /** Currency for the receiving amount */ + fun receivingCurrency(receivingCurrency: Currency) = + receivingCurrency(JsonField.of(receivingCurrency)) + + /** + * Sets [Builder.receivingCurrency] to an arbitrary JSON value. + * + * You should usually call [Builder.receivingCurrency] with a well-typed [Currency] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receivingCurrency(receivingCurrency: JsonField) = apply { + this.receivingCurrency = receivingCurrency + } + + /** Currency for the sending amount */ + fun sendingCurrency(sendingCurrency: Currency) = + sendingCurrency(JsonField.of(sendingCurrency)) + + /** + * Sets [Builder.sendingCurrency] to an arbitrary JSON value. + * + * You should usually call [Builder.sendingCurrency] with a well-typed [Currency] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sendingCurrency(sendingCurrency: JsonField) = apply { + this.sendingCurrency = sendingCurrency + } + + /** Source account details */ + fun source(source: QuoteSourceOneOf) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [QuoteSourceOneOf] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** Alias for calling [source] with `QuoteSourceOneOf.ofAccount(account)`. */ + fun source(account: QuoteSourceOneOf.Account) = source(QuoteSourceOneOf.ofAccount(account)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * QuoteSourceOneOf.Account.builder() + * .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountSource(accountId: String) = + source( + QuoteSourceOneOf.Account.builder() + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** + * Alias for calling [source] with `QuoteSourceOneOf.ofRealtimeFunding(realtimeFunding)`. + */ + fun source(realtimeFunding: QuoteSourceOneOf.RealtimeFunding) = + source(QuoteSourceOneOf.ofRealtimeFunding(realtimeFunding)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * QuoteSourceOneOf.RealtimeFunding.builder() + * .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + * .currency(currency) + * .build() + * ``` + */ + fun realtimeFundingSource(currency: String) = + source( + QuoteSourceOneOf.RealtimeFunding.builder() + .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + .currency(currency) + .build() + ) + + /** Current status of the quote */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * The total amount that will be received in the smallest unit of the receiving currency + * (eg. cents). + */ + fun totalReceivingAmount(totalReceivingAmount: Long) = + totalReceivingAmount(JsonField.of(totalReceivingAmount)) + + /** + * Sets [Builder.totalReceivingAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalReceivingAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun totalReceivingAmount(totalReceivingAmount: JsonField) = apply { + this.totalReceivingAmount = totalReceivingAmount + } + + /** + * The total amount that will be sent in the smallest unit of the sending currency (eg. + * cents). + */ + fun totalSendingAmount(totalSendingAmount: Long) = + totalSendingAmount(JsonField.of(totalSendingAmount)) + + /** + * Sets [Builder.totalSendingAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalSendingAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun totalSendingAmount(totalSendingAmount: JsonField) = apply { + this.totalSendingAmount = totalSendingAmount + } + + /** The ID of the transaction created from this quote. */ + fun transactionId(transactionId: String) = transactionId(JsonField.of(transactionId)) + + /** + * Sets [Builder.transactionId] to an arbitrary JSON value. + * + * You should usually call [Builder.transactionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun transactionId(transactionId: JsonField) = apply { + this.transactionId = transactionId + } + + /** ID of the quote that is being retried */ + fun originalQuoteId(originalQuoteId: String) = + originalQuoteId(JsonField.of(originalQuoteId)) + + /** + * Sets [Builder.originalQuoteId] to an arbitrary JSON value. + * + * You should usually call [Builder.originalQuoteId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun originalQuoteId(originalQuoteId: JsonField) = apply { + this.originalQuoteId = originalQuoteId + } + + /** + * Payment instructions for executing the payment. This is not required when using an + * internal account source. + */ + fun paymentInstructions(paymentInstructions: List) = + paymentInstructions(JsonField.of(paymentInstructions)) + + /** + * Sets [Builder.paymentInstructions] to an arbitrary JSON value. + * + * You should usually call [Builder.paymentInstructions] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun paymentInstructions(paymentInstructions: JsonField>) = apply { + this.paymentInstructions = paymentInstructions.map { it.toMutableList() } + } + + /** + * Adds a single [PaymentInstructions] to [paymentInstructions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addPaymentInstruction(paymentInstruction: PaymentInstructions) = apply { + paymentInstructions = + (paymentInstructions ?: JsonField.of(mutableListOf())).also { + checkKnown("paymentInstructions", it).add(paymentInstruction) + } + } + + /** Details about the rate and fees for the transaction. */ + fun rateDetails(rateDetails: OutgoingRateDetails) = rateDetails(JsonField.of(rateDetails)) + + /** + * Sets [Builder.rateDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.rateDetails] with a well-typed [OutgoingRateDetails] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun rateDetails(rateDetails: JsonField) = apply { + this.rateDetails = rateDetails + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Quote]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .createdAt() + * .destination() + * .exchangeRate() + * .expiresAt() + * .feesIncluded() + * .quoteId() + * .receivingCurrency() + * .sendingCurrency() + * .source() + * .status() + * .totalReceivingAmount() + * .totalSendingAmount() + * .transactionId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Quote = + Quote( + checkRequired("createdAt", createdAt), + checkRequired("destination", destination), + checkRequired("exchangeRate", exchangeRate), + checkRequired("expiresAt", expiresAt), + checkRequired("feesIncluded", feesIncluded), + checkRequired("quoteId", quoteId), + checkRequired("receivingCurrency", receivingCurrency), + checkRequired("sendingCurrency", sendingCurrency), + checkRequired("source", source), + checkRequired("status", status), + checkRequired("totalReceivingAmount", totalReceivingAmount), + checkRequired("totalSendingAmount", totalSendingAmount), + checkRequired("transactionId", transactionId), + originalQuoteId, + (paymentInstructions ?: JsonMissing.of()).map { it.toImmutable() }, + rateDetails, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Quote = apply { + if (validated) { + return@apply + } + + createdAt() + destination().validate() + exchangeRate() + expiresAt() + feesIncluded() + quoteId() + receivingCurrency().validate() + sendingCurrency().validate() + source().validate() + status().validate() + totalReceivingAmount() + totalSendingAmount() + transactionId() + originalQuoteId() + paymentInstructions()?.forEach { it.validate() } + rateDetails()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (createdAt.asKnown() == null) 0 else 1) + + (destination.asKnown()?.validity() ?: 0) + + (if (exchangeRate.asKnown() == null) 0 else 1) + + (if (expiresAt.asKnown() == null) 0 else 1) + + (if (feesIncluded.asKnown() == null) 0 else 1) + + (if (quoteId.asKnown() == null) 0 else 1) + + (receivingCurrency.asKnown()?.validity() ?: 0) + + (sendingCurrency.asKnown()?.validity() ?: 0) + + (source.asKnown()?.validity() ?: 0) + + (status.asKnown()?.validity() ?: 0) + + (if (totalReceivingAmount.asKnown() == null) 0 else 1) + + (if (totalSendingAmount.asKnown() == null) 0 else 1) + + (if (transactionId.asKnown() == null) 0 else 1) + + (if (originalQuoteId.asKnown() == null) 0 else 1) + + (paymentInstructions.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (rateDetails.asKnown()?.validity() ?: 0) + + /** Current status of the quote */ + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PENDING = of("PENDING") + + val PROCESSING = of("PROCESSING") + + val COMPLETED = of("COMPLETED") + + val FAILED = of("FAILED") + + val EXPIRED = of("EXPIRED") + + fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + PENDING, + PROCESSING, + COMPLETED, + FAILED, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PENDING, + PROCESSING, + COMPLETED, + FAILED, + EXPIRED, + /** An enum member indicating that [Status] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PENDING -> Value.PENDING + PROCESSING -> Value.PROCESSING + COMPLETED -> Value.COMPLETED + FAILED -> Value.FAILED + EXPIRED -> Value.EXPIRED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + PENDING -> Known.PENDING + PROCESSING -> Known.PROCESSING + COMPLETED -> Known.COMPLETED + FAILED -> Known.FAILED + EXPIRED -> Known.EXPIRED + else -> throw GridInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Quote && + createdAt == other.createdAt && + destination == other.destination && + exchangeRate == other.exchangeRate && + expiresAt == other.expiresAt && + feesIncluded == other.feesIncluded && + quoteId == other.quoteId && + receivingCurrency == other.receivingCurrency && + sendingCurrency == other.sendingCurrency && + source == other.source && + status == other.status && + totalReceivingAmount == other.totalReceivingAmount && + totalSendingAmount == other.totalSendingAmount && + transactionId == other.transactionId && + originalQuoteId == other.originalQuoteId && + paymentInstructions == other.paymentInstructions && + rateDetails == other.rateDetails && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + createdAt, + destination, + exchangeRate, + expiresAt, + feesIncluded, + quoteId, + receivingCurrency, + sendingCurrency, + source, + status, + totalReceivingAmount, + totalSendingAmount, + transactionId, + originalQuoteId, + paymentInstructions, + rateDetails, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Quote{createdAt=$createdAt, destination=$destination, exchangeRate=$exchangeRate, expiresAt=$expiresAt, feesIncluded=$feesIncluded, quoteId=$quoteId, receivingCurrency=$receivingCurrency, sendingCurrency=$sendingCurrency, source=$source, status=$status, totalReceivingAmount=$totalReceivingAmount, totalSendingAmount=$totalSendingAmount, transactionId=$transactionId, originalQuoteId=$originalQuoteId, paymentInstructions=$paymentInstructions, rateDetails=$rateDetails, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteCreateParams.kt new file mode 100644 index 00000000..41a86ddc --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteCreateParams.kt @@ -0,0 +1,1532 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import java.util.Collections +import java.util.Objects + +/** + * Generate a quote for a cross-currency transfer between any combination of accounts and UMA + * addresses. This endpoint handles currency exchange and provides the necessary instructions to + * execute the transfer. + * + * **Transfer Types Supported:** + * - **Account to Account**: Transfer between internal/external accounts with currency exchange. + * - **Account to UMA**: Transfer from an internal account to an UMA address. + * - **UMA to Account or UMA to UMA**: This transfer type will only be funded by payment + * instructions, not from an internal account. + * + * **Key Features:** + * - **Flexible Amount Locking**: Always specify whether you want to lock the sending amount or + * receiving amount + * - **Currency Exchange**: Handles all cross-currency transfers with real-time exchange rates + * - **Payment Instructions**: For UMA or customer ID sources, provides banking details needed for + * execution + * + * **Important:** If you are transferring funds in the same currency (no exchange required), use the + * `/transfer-in` or `/transfer-out` endpoints instead. + * + * **Sandbox Testing:** When using the `externalAccountDetails` destination type in sandbox mode, + * use account number patterns ending in specific digits to test different scenarios. These patterns + * should be used with the primary alias, address, or identifier of whatever account type you're + * testing. For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The + * failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ +class QuoteCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Destination account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): QuoteDestinationOneOf = body.destination() + + /** + * The amount to send/receive in the smallest unit of the locked currency (eg. cents). See + * `lockedCurrencySide` for more information. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun lockedCurrencyAmount(): Long = body.lockedCurrencyAmount() + + /** + * The side of the quote which should be locked and specified in the `lockedCurrencyAmount`. For + * example, if I want to send exactly $5 MXN from my wallet, I would set this to "sending", and + * the `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to receive exactly $10 + * USD, I would set this to "receiving" and the `lockedCurrencyAmount` to 10000 (in cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun lockedCurrencySide(): LockedCurrencySide = body.lockedCurrencySide() + + /** + * Source account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): QuoteSourceOneOf = body.source() + + /** + * Optional description/memo for the transfer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun description(): String? = body.description() + + /** + * Whether to immediately execute the quote after creation. If true, the quote will be executed + * and the transaction will be created at the current exchange rate. It should only be used if + * you don't want to lock and view rate details before executing the quote. If you are executing + * a pre-existing quote, use the `/quotes/{quoteId}/execute` endpoint instead. This is false by + * default. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun immediatelyExecute(): Boolean? = body.immediatelyExecute() + + /** + * Lookup ID from a previous receiver lookup request. If provided, this can make the quote + * creation more efficient by reusing cached lookup data. NOTE: This is required for UMA + * destinations due to counterparty institution requirements. See `senderCustomerInfo` for more + * information. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun lookupId(): String? = body.lookupId() + + /** + * Only relevant for UMA destinations. Key-value pairs of information about the sender which was + * requested by the counterparty (recipient) institution. Any fields specified in + * `requiredPayerDataFields` from the response of the `/receiver/uma/{receiverUmaAddress}` + * (lookupUma) endpoint MUST be provided here if they were requested. If the counterparty + * (recipient) institution did not request any information, this field can be omitted. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun senderCustomerInfo(): SenderCustomerInfo? = body.senderCustomerInfo() + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _destination(): JsonField = body._destination() + + /** + * Returns the raw JSON value of [lockedCurrencyAmount]. + * + * Unlike [lockedCurrencyAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _lockedCurrencyAmount(): JsonField = body._lockedCurrencyAmount() + + /** + * Returns the raw JSON value of [lockedCurrencySide]. + * + * Unlike [lockedCurrencySide], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _lockedCurrencySide(): JsonField = body._lockedCurrencySide() + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _source(): JsonField = body._source() + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _description(): JsonField = body._description() + + /** + * Returns the raw JSON value of [immediatelyExecute]. + * + * Unlike [immediatelyExecute], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _immediatelyExecute(): JsonField = body._immediatelyExecute() + + /** + * Returns the raw JSON value of [lookupId]. + * + * Unlike [lookupId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _lookupId(): JsonField = body._lookupId() + + /** + * Returns the raw JSON value of [senderCustomerInfo]. + * + * Unlike [senderCustomerInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _senderCustomerInfo(): JsonField = body._senderCustomerInfo() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [QuoteCreateParams]. + * + * The following fields are required: + * ```kotlin + * .destination() + * .lockedCurrencyAmount() + * .lockedCurrencySide() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [QuoteCreateParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(quoteCreateParams: QuoteCreateParams) = apply { + body = quoteCreateParams.body.toBuilder() + additionalHeaders = quoteCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = quoteCreateParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [destination] + * - [lockedCurrencyAmount] + * - [lockedCurrencySide] + * - [source] + * - [description] + * - etc. + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Destination account details */ + fun destination(destination: QuoteDestinationOneOf) = apply { + body.destination(destination) + } + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [QuoteDestinationOneOf] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun destination(destination: JsonField) = apply { + body.destination(destination) + } + + /** Alias for calling [destination] with `QuoteDestinationOneOf.ofAccount(account)`. */ + fun destination(account: QuoteDestinationOneOf.Account) = apply { + body.destination(account) + } + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.Account.builder() + * .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountDestination(accountId: String) = apply { body.accountDestination(accountId) } + + /** + * Alias for calling [destination] with `QuoteDestinationOneOf.ofUmaAddress(umaAddress)`. + */ + fun destination(umaAddress: QuoteDestinationOneOf.UmaAddress) = apply { + body.destination(umaAddress) + } + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.UmaAddress.builder() + * .destinationType(QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressDestination(umaAddress: String) = apply { + body.umaAddressDestination(umaAddress) + } + + /** + * Alias for calling [destination] with + * `QuoteDestinationOneOf.ofExternalAccountDetails(externalAccountDetails)`. + */ + fun destination(externalAccountDetails: QuoteDestinationOneOf.ExternalAccountDetails) = + apply { + body.destination(externalAccountDetails) + } + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.ExternalAccountDetails.builder() + * .destinationType(QuoteDestinationOneOf.ExternalAccountDetails.DestinationType.EXTERNAL_ACCOUNT_DETAILS) + * .externalAccountDetails(externalAccountDetails) + * .build() + * ``` + */ + fun externalAccountDetailsDestination(externalAccountDetails: ExternalAccountCreate) = + apply { + body.externalAccountDetailsDestination(externalAccountDetails) + } + + /** + * The amount to send/receive in the smallest unit of the locked currency (eg. cents). See + * `lockedCurrencySide` for more information. + */ + fun lockedCurrencyAmount(lockedCurrencyAmount: Long) = apply { + body.lockedCurrencyAmount(lockedCurrencyAmount) + } + + /** + * Sets [Builder.lockedCurrencyAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.lockedCurrencyAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lockedCurrencyAmount(lockedCurrencyAmount: JsonField) = apply { + body.lockedCurrencyAmount(lockedCurrencyAmount) + } + + /** + * The side of the quote which should be locked and specified in the `lockedCurrencyAmount`. + * For example, if I want to send exactly $5 MXN from my wallet, I would set this to + * "sending", and the `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to + * receive exactly $10 USD, I would set this to "receiving" and the `lockedCurrencyAmount` + * to 10000 (in cents). + */ + fun lockedCurrencySide(lockedCurrencySide: LockedCurrencySide) = apply { + body.lockedCurrencySide(lockedCurrencySide) + } + + /** + * Sets [Builder.lockedCurrencySide] to an arbitrary JSON value. + * + * You should usually call [Builder.lockedCurrencySide] with a well-typed + * [LockedCurrencySide] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun lockedCurrencySide(lockedCurrencySide: JsonField) = apply { + body.lockedCurrencySide(lockedCurrencySide) + } + + /** Source account details */ + fun source(source: QuoteSourceOneOf) = apply { body.source(source) } + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [QuoteSourceOneOf] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { body.source(source) } + + /** Alias for calling [source] with `QuoteSourceOneOf.ofAccount(account)`. */ + fun source(account: QuoteSourceOneOf.Account) = apply { body.source(account) } + + /** + * Alias for calling [source] with the following: + * ```kotlin + * QuoteSourceOneOf.Account.builder() + * .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountSource(accountId: String) = apply { body.accountSource(accountId) } + + /** + * Alias for calling [source] with `QuoteSourceOneOf.ofRealtimeFunding(realtimeFunding)`. + */ + fun source(realtimeFunding: QuoteSourceOneOf.RealtimeFunding) = apply { + body.source(realtimeFunding) + } + + /** + * Alias for calling [source] with the following: + * ```kotlin + * QuoteSourceOneOf.RealtimeFunding.builder() + * .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + * .currency(currency) + * .build() + * ``` + */ + fun realtimeFundingSource(currency: String) = apply { body.realtimeFundingSource(currency) } + + /** Optional description/memo for the transfer */ + fun description(description: String) = apply { body.description(description) } + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun description(description: JsonField) = apply { body.description(description) } + + /** + * Whether to immediately execute the quote after creation. If true, the quote will be + * executed and the transaction will be created at the current exchange rate. It should only + * be used if you don't want to lock and view rate details before executing the quote. If + * you are executing a pre-existing quote, use the `/quotes/{quoteId}/execute` endpoint + * instead. This is false by default. + */ + fun immediatelyExecute(immediatelyExecute: Boolean) = apply { + body.immediatelyExecute(immediatelyExecute) + } + + /** + * Sets [Builder.immediatelyExecute] to an arbitrary JSON value. + * + * You should usually call [Builder.immediatelyExecute] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun immediatelyExecute(immediatelyExecute: JsonField) = apply { + body.immediatelyExecute(immediatelyExecute) + } + + /** + * Lookup ID from a previous receiver lookup request. If provided, this can make the quote + * creation more efficient by reusing cached lookup data. NOTE: This is required for UMA + * destinations due to counterparty institution requirements. See `senderCustomerInfo` for + * more information. + */ + fun lookupId(lookupId: String) = apply { body.lookupId(lookupId) } + + /** + * Sets [Builder.lookupId] to an arbitrary JSON value. + * + * You should usually call [Builder.lookupId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun lookupId(lookupId: JsonField) = apply { body.lookupId(lookupId) } + + /** + * Only relevant for UMA destinations. Key-value pairs of information about the sender which + * was requested by the counterparty (recipient) institution. Any fields specified in + * `requiredPayerDataFields` from the response of the `/receiver/uma/{receiverUmaAddress}` + * (lookupUma) endpoint MUST be provided here if they were requested. If the counterparty + * (recipient) institution did not request any information, this field can be omitted. + */ + fun senderCustomerInfo(senderCustomerInfo: SenderCustomerInfo) = apply { + body.senderCustomerInfo(senderCustomerInfo) + } + + /** + * Sets [Builder.senderCustomerInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.senderCustomerInfo] with a well-typed + * [SenderCustomerInfo] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun senderCustomerInfo(senderCustomerInfo: JsonField) = apply { + body.senderCustomerInfo(senderCustomerInfo) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [QuoteCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destination() + * .lockedCurrencyAmount() + * .lockedCurrencySide() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): QuoteCreateParams = + QuoteCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val destination: JsonField, + private val lockedCurrencyAmount: JsonField, + private val lockedCurrencySide: JsonField, + private val source: JsonField, + private val description: JsonField, + private val immediatelyExecute: JsonField, + private val lookupId: JsonField, + private val senderCustomerInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("lockedCurrencyAmount") + @ExcludeMissing + lockedCurrencyAmount: JsonField = JsonMissing.of(), + @JsonProperty("lockedCurrencySide") + @ExcludeMissing + lockedCurrencySide: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("description") + @ExcludeMissing + description: JsonField = JsonMissing.of(), + @JsonProperty("immediatelyExecute") + @ExcludeMissing + immediatelyExecute: JsonField = JsonMissing.of(), + @JsonProperty("lookupId") + @ExcludeMissing + lookupId: JsonField = JsonMissing.of(), + @JsonProperty("senderCustomerInfo") + @ExcludeMissing + senderCustomerInfo: JsonField = JsonMissing.of(), + ) : this( + destination, + lockedCurrencyAmount, + lockedCurrencySide, + source, + description, + immediatelyExecute, + lookupId, + senderCustomerInfo, + mutableMapOf(), + ) + + /** + * Destination account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): QuoteDestinationOneOf = destination.getRequired("destination") + + /** + * The amount to send/receive in the smallest unit of the locked currency (eg. cents). See + * `lockedCurrencySide` for more information. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun lockedCurrencyAmount(): Long = lockedCurrencyAmount.getRequired("lockedCurrencyAmount") + + /** + * The side of the quote which should be locked and specified in the `lockedCurrencyAmount`. + * For example, if I want to send exactly $5 MXN from my wallet, I would set this to + * "sending", and the `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to + * receive exactly $10 USD, I would set this to "receiving" and the `lockedCurrencyAmount` + * to 10000 (in cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun lockedCurrencySide(): LockedCurrencySide = + lockedCurrencySide.getRequired("lockedCurrencySide") + + /** + * Source account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): QuoteSourceOneOf = source.getRequired("source") + + /** + * Optional description/memo for the transfer + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun description(): String? = description.getNullable("description") + + /** + * Whether to immediately execute the quote after creation. If true, the quote will be + * executed and the transaction will be created at the current exchange rate. It should only + * be used if you don't want to lock and view rate details before executing the quote. If + * you are executing a pre-existing quote, use the `/quotes/{quoteId}/execute` endpoint + * instead. This is false by default. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun immediatelyExecute(): Boolean? = immediatelyExecute.getNullable("immediatelyExecute") + + /** + * Lookup ID from a previous receiver lookup request. If provided, this can make the quote + * creation more efficient by reusing cached lookup data. NOTE: This is required for UMA + * destinations due to counterparty institution requirements. See `senderCustomerInfo` for + * more information. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lookupId(): String? = lookupId.getNullable("lookupId") + + /** + * Only relevant for UMA destinations. Key-value pairs of information about the sender which + * was requested by the counterparty (recipient) institution. Any fields specified in + * `requiredPayerDataFields` from the response of the `/receiver/uma/{receiverUmaAddress}` + * (lookupUma) endpoint MUST be provided here if they were requested. If the counterparty + * (recipient) institution did not request any information, this field can be omitted. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun senderCustomerInfo(): SenderCustomerInfo? = + senderCustomerInfo.getNullable("senderCustomerInfo") + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [lockedCurrencyAmount]. + * + * Unlike [lockedCurrencyAmount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("lockedCurrencyAmount") + @ExcludeMissing + fun _lockedCurrencyAmount(): JsonField = lockedCurrencyAmount + + /** + * Returns the raw JSON value of [lockedCurrencySide]. + * + * Unlike [lockedCurrencySide], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("lockedCurrencySide") + @ExcludeMissing + fun _lockedCurrencySide(): JsonField = lockedCurrencySide + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("description") + @ExcludeMissing + fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [immediatelyExecute]. + * + * Unlike [immediatelyExecute], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("immediatelyExecute") + @ExcludeMissing + fun _immediatelyExecute(): JsonField = immediatelyExecute + + /** + * Returns the raw JSON value of [lookupId]. + * + * Unlike [lookupId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lookupId") @ExcludeMissing fun _lookupId(): JsonField = lookupId + + /** + * Returns the raw JSON value of [senderCustomerInfo]. + * + * Unlike [senderCustomerInfo], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("senderCustomerInfo") + @ExcludeMissing + fun _senderCustomerInfo(): JsonField = senderCustomerInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .destination() + * .lockedCurrencyAmount() + * .lockedCurrencySide() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var destination: JsonField? = null + private var lockedCurrencyAmount: JsonField? = null + private var lockedCurrencySide: JsonField? = null + private var source: JsonField? = null + private var description: JsonField = JsonMissing.of() + private var immediatelyExecute: JsonField = JsonMissing.of() + private var lookupId: JsonField = JsonMissing.of() + private var senderCustomerInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + destination = body.destination + lockedCurrencyAmount = body.lockedCurrencyAmount + lockedCurrencySide = body.lockedCurrencySide + source = body.source + description = body.description + immediatelyExecute = body.immediatelyExecute + lookupId = body.lookupId + senderCustomerInfo = body.senderCustomerInfo + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Destination account details */ + fun destination(destination: QuoteDestinationOneOf) = + destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed + * [QuoteDestinationOneOf] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Alias for calling [destination] with `QuoteDestinationOneOf.ofAccount(account)`. */ + fun destination(account: QuoteDestinationOneOf.Account) = + destination(QuoteDestinationOneOf.ofAccount(account)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.Account.builder() + * .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountDestination(accountId: String) = + destination( + QuoteDestinationOneOf.Account.builder() + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** + * Alias for calling [destination] with + * `QuoteDestinationOneOf.ofUmaAddress(umaAddress)`. + */ + fun destination(umaAddress: QuoteDestinationOneOf.UmaAddress) = + destination(QuoteDestinationOneOf.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.UmaAddress.builder() + * .destinationType(QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressDestination(umaAddress: String) = + destination( + QuoteDestinationOneOf.UmaAddress.builder() + .destinationType( + QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS + ) + .umaAddress(umaAddress) + .build() + ) + + /** + * Alias for calling [destination] with + * `QuoteDestinationOneOf.ofExternalAccountDetails(externalAccountDetails)`. + */ + fun destination(externalAccountDetails: QuoteDestinationOneOf.ExternalAccountDetails) = + destination(QuoteDestinationOneOf.ofExternalAccountDetails(externalAccountDetails)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * QuoteDestinationOneOf.ExternalAccountDetails.builder() + * .destinationType(QuoteDestinationOneOf.ExternalAccountDetails.DestinationType.EXTERNAL_ACCOUNT_DETAILS) + * .externalAccountDetails(externalAccountDetails) + * .build() + * ``` + */ + fun externalAccountDetailsDestination(externalAccountDetails: ExternalAccountCreate) = + destination( + QuoteDestinationOneOf.ExternalAccountDetails.builder() + .destinationType( + QuoteDestinationOneOf.ExternalAccountDetails.DestinationType + .EXTERNAL_ACCOUNT_DETAILS + ) + .externalAccountDetails(externalAccountDetails) + .build() + ) + + /** + * The amount to send/receive in the smallest unit of the locked currency (eg. cents). + * See `lockedCurrencySide` for more information. + */ + fun lockedCurrencyAmount(lockedCurrencyAmount: Long) = + lockedCurrencyAmount(JsonField.of(lockedCurrencyAmount)) + + /** + * Sets [Builder.lockedCurrencyAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.lockedCurrencyAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lockedCurrencyAmount(lockedCurrencyAmount: JsonField) = apply { + this.lockedCurrencyAmount = lockedCurrencyAmount + } + + /** + * The side of the quote which should be locked and specified in the + * `lockedCurrencyAmount`. For example, if I want to send exactly $5 MXN from my wallet, + * I would set this to "sending", and the `lockedCurrencyAmount` to 500 (in cents). If I + * want the receiver to receive exactly $10 USD, I would set this to "receiving" and the + * `lockedCurrencyAmount` to 10000 (in cents). + */ + fun lockedCurrencySide(lockedCurrencySide: LockedCurrencySide) = + lockedCurrencySide(JsonField.of(lockedCurrencySide)) + + /** + * Sets [Builder.lockedCurrencySide] to an arbitrary JSON value. + * + * You should usually call [Builder.lockedCurrencySide] with a well-typed + * [LockedCurrencySide] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun lockedCurrencySide(lockedCurrencySide: JsonField) = apply { + this.lockedCurrencySide = lockedCurrencySide + } + + /** Source account details */ + fun source(source: QuoteSourceOneOf) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [QuoteSourceOneOf] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** Alias for calling [source] with `QuoteSourceOneOf.ofAccount(account)`. */ + fun source(account: QuoteSourceOneOf.Account) = + source(QuoteSourceOneOf.ofAccount(account)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * QuoteSourceOneOf.Account.builder() + * .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountSource(accountId: String) = + source( + QuoteSourceOneOf.Account.builder() + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** + * Alias for calling [source] with + * `QuoteSourceOneOf.ofRealtimeFunding(realtimeFunding)`. + */ + fun source(realtimeFunding: QuoteSourceOneOf.RealtimeFunding) = + source(QuoteSourceOneOf.ofRealtimeFunding(realtimeFunding)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * QuoteSourceOneOf.RealtimeFunding.builder() + * .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + * .currency(currency) + * .build() + * ``` + */ + fun realtimeFundingSource(currency: String) = + source( + QuoteSourceOneOf.RealtimeFunding.builder() + .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + .currency(currency) + .build() + ) + + /** Optional description/memo for the transfer */ + fun description(description: String) = description(JsonField.of(description)) + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun description(description: JsonField) = apply { + this.description = description + } + + /** + * Whether to immediately execute the quote after creation. If true, the quote will be + * executed and the transaction will be created at the current exchange rate. It should + * only be used if you don't want to lock and view rate details before executing the + * quote. If you are executing a pre-existing quote, use the `/quotes/{quoteId}/execute` + * endpoint instead. This is false by default. + */ + fun immediatelyExecute(immediatelyExecute: Boolean) = + immediatelyExecute(JsonField.of(immediatelyExecute)) + + /** + * Sets [Builder.immediatelyExecute] to an arbitrary JSON value. + * + * You should usually call [Builder.immediatelyExecute] with a well-typed [Boolean] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun immediatelyExecute(immediatelyExecute: JsonField) = apply { + this.immediatelyExecute = immediatelyExecute + } + + /** + * Lookup ID from a previous receiver lookup request. If provided, this can make the + * quote creation more efficient by reusing cached lookup data. NOTE: This is required + * for UMA destinations due to counterparty institution requirements. See + * `senderCustomerInfo` for more information. + */ + fun lookupId(lookupId: String) = lookupId(JsonField.of(lookupId)) + + /** + * Sets [Builder.lookupId] to an arbitrary JSON value. + * + * You should usually call [Builder.lookupId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lookupId(lookupId: JsonField) = apply { this.lookupId = lookupId } + + /** + * Only relevant for UMA destinations. Key-value pairs of information about the sender + * which was requested by the counterparty (recipient) institution. Any fields specified + * in `requiredPayerDataFields` from the response of the + * `/receiver/uma/{receiverUmaAddress}` (lookupUma) endpoint MUST be provided here if + * they were requested. If the counterparty (recipient) institution did not request any + * information, this field can be omitted. + */ + fun senderCustomerInfo(senderCustomerInfo: SenderCustomerInfo) = + senderCustomerInfo(JsonField.of(senderCustomerInfo)) + + /** + * Sets [Builder.senderCustomerInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.senderCustomerInfo] with a well-typed + * [SenderCustomerInfo] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun senderCustomerInfo(senderCustomerInfo: JsonField) = apply { + this.senderCustomerInfo = senderCustomerInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destination() + * .lockedCurrencyAmount() + * .lockedCurrencySide() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("destination", destination), + checkRequired("lockedCurrencyAmount", lockedCurrencyAmount), + checkRequired("lockedCurrencySide", lockedCurrencySide), + checkRequired("source", source), + description, + immediatelyExecute, + lookupId, + senderCustomerInfo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + destination().validate() + lockedCurrencyAmount() + lockedCurrencySide().validate() + source().validate() + description() + immediatelyExecute() + lookupId() + senderCustomerInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (destination.asKnown()?.validity() ?: 0) + + (if (lockedCurrencyAmount.asKnown() == null) 0 else 1) + + (lockedCurrencySide.asKnown()?.validity() ?: 0) + + (source.asKnown()?.validity() ?: 0) + + (if (description.asKnown() == null) 0 else 1) + + (if (immediatelyExecute.asKnown() == null) 0 else 1) + + (if (lookupId.asKnown() == null) 0 else 1) + + (senderCustomerInfo.asKnown()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + destination == other.destination && + lockedCurrencyAmount == other.lockedCurrencyAmount && + lockedCurrencySide == other.lockedCurrencySide && + source == other.source && + description == other.description && + immediatelyExecute == other.immediatelyExecute && + lookupId == other.lookupId && + senderCustomerInfo == other.senderCustomerInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + destination, + lockedCurrencyAmount, + lockedCurrencySide, + source, + description, + immediatelyExecute, + lookupId, + senderCustomerInfo, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{destination=$destination, lockedCurrencyAmount=$lockedCurrencyAmount, lockedCurrencySide=$lockedCurrencySide, source=$source, description=$description, immediatelyExecute=$immediatelyExecute, lookupId=$lookupId, senderCustomerInfo=$senderCustomerInfo, additionalProperties=$additionalProperties}" + } + + /** + * The side of the quote which should be locked and specified in the `lockedCurrencyAmount`. For + * example, if I want to send exactly $5 MXN from my wallet, I would set this to "sending", and + * the `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to receive exactly $10 + * USD, I would set this to "receiving" and the `lockedCurrencyAmount` to 10000 (in cents). + */ + class LockedCurrencySide + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val SENDING = of("SENDING") + + val RECEIVING = of("RECEIVING") + + fun of(value: String) = LockedCurrencySide(JsonField.of(value)) + } + + /** An enum containing [LockedCurrencySide]'s known values. */ + enum class Known { + SENDING, + RECEIVING, + } + + /** + * An enum containing [LockedCurrencySide]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [LockedCurrencySide] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + SENDING, + RECEIVING, + /** + * An enum member indicating that [LockedCurrencySide] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + SENDING -> Value.SENDING + RECEIVING -> Value.RECEIVING + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + SENDING -> Known.SENDING + RECEIVING -> Known.RECEIVING + else -> throw GridInvalidDataException("Unknown LockedCurrencySide: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): LockedCurrencySide = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LockedCurrencySide && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** + * Only relevant for UMA destinations. Key-value pairs of information about the sender which was + * requested by the counterparty (recipient) institution. Any fields specified in + * `requiredPayerDataFields` from the response of the `/receiver/uma/{receiverUmaAddress}` + * (lookupUma) endpoint MUST be provided here if they were requested. If the counterparty + * (recipient) institution did not request any information, this field can be omitted. + */ + class SenderCustomerInfo + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [SenderCustomerInfo]. */ + fun builder() = Builder() + } + + /** A builder for [SenderCustomerInfo]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(senderCustomerInfo: SenderCustomerInfo) = apply { + additionalProperties = senderCustomerInfo.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SenderCustomerInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): SenderCustomerInfo = SenderCustomerInfo(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): SenderCustomerInfo = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SenderCustomerInfo && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "SenderCustomerInfo{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteCreateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "QuoteCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOf.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOf.kt new file mode 100644 index 00000000..8147410a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOf.kt @@ -0,0 +1,1481 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import java.util.Collections +import java.util.Objects + +/** Destination account details */ +@JsonDeserialize(using = QuoteDestinationOneOf.Deserializer::class) +@JsonSerialize(using = QuoteDestinationOneOf.Serializer::class) +class QuoteDestinationOneOf +private constructor( + private val account: Account? = null, + private val umaAddress: UmaAddress? = null, + private val externalAccountDetails: ExternalAccountDetails? = null, + private val _json: JsonValue? = null, +) { + + /** Destination account details */ + fun account(): Account? = account + + /** UMA address destination details */ + fun umaAddress(): UmaAddress? = umaAddress + + /** + * A convenient destination option which adds the external account and creates the quote in one + * step rather than first needing to call /external-accounts to add the account. Useful for + * one-off payments to some destination. See the external accounts endpoints for test values in + * sandbox mode. + */ + fun externalAccountDetails(): ExternalAccountDetails? = externalAccountDetails + + fun isAccount(): Boolean = account != null + + fun isUmaAddress(): Boolean = umaAddress != null + + fun isExternalAccountDetails(): Boolean = externalAccountDetails != null + + /** Destination account details */ + fun asAccount(): Account = account.getOrThrow("account") + + /** UMA address destination details */ + fun asUmaAddress(): UmaAddress = umaAddress.getOrThrow("umaAddress") + + /** + * A convenient destination option which adds the external account and creates the quote in one + * step rather than first needing to call /external-accounts to add the account. Useful for + * one-off payments to some destination. See the external accounts endpoints for test values in + * sandbox mode. + */ + fun asExternalAccountDetails(): ExternalAccountDetails = + externalAccountDetails.getOrThrow("externalAccountDetails") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + account != null -> visitor.visitAccount(account) + umaAddress != null -> visitor.visitUmaAddress(umaAddress) + externalAccountDetails != null -> + visitor.visitExternalAccountDetails(externalAccountDetails) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): QuoteDestinationOneOf = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAccount(account: Account) { + account.validate() + } + + override fun visitUmaAddress(umaAddress: UmaAddress) { + umaAddress.validate() + } + + override fun visitExternalAccountDetails( + externalAccountDetails: ExternalAccountDetails + ) { + externalAccountDetails.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAccount(account: Account) = account.validity() + + override fun visitUmaAddress(umaAddress: UmaAddress) = umaAddress.validity() + + override fun visitExternalAccountDetails( + externalAccountDetails: ExternalAccountDetails + ) = externalAccountDetails.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteDestinationOneOf && + account == other.account && + umaAddress == other.umaAddress && + externalAccountDetails == other.externalAccountDetails + } + + override fun hashCode(): Int = Objects.hash(account, umaAddress, externalAccountDetails) + + override fun toString(): String = + when { + account != null -> "QuoteDestinationOneOf{account=$account}" + umaAddress != null -> "QuoteDestinationOneOf{umaAddress=$umaAddress}" + externalAccountDetails != null -> + "QuoteDestinationOneOf{externalAccountDetails=$externalAccountDetails}" + _json != null -> "QuoteDestinationOneOf{_unknown=$_json}" + else -> throw IllegalStateException("Invalid QuoteDestinationOneOf") + } + + companion object { + + /** Destination account details */ + fun ofAccount(account: Account) = QuoteDestinationOneOf(account = account) + + /** UMA address destination details */ + fun ofUmaAddress(umaAddress: UmaAddress) = QuoteDestinationOneOf(umaAddress = umaAddress) + + /** + * A convenient destination option which adds the external account and creates the quote in + * one step rather than first needing to call /external-accounts to add the account. Useful + * for one-off payments to some destination. See the external accounts endpoints for test + * values in sandbox mode. + */ + fun ofExternalAccountDetails(externalAccountDetails: ExternalAccountDetails) = + QuoteDestinationOneOf(externalAccountDetails = externalAccountDetails) + } + + /** + * An interface that defines how to map each variant of [QuoteDestinationOneOf] to a value of + * type [T]. + */ + interface Visitor { + + /** Destination account details */ + fun visitAccount(account: Account): T + + /** UMA address destination details */ + fun visitUmaAddress(umaAddress: UmaAddress): T + + /** + * A convenient destination option which adds the external account and creates the quote in + * one step rather than first needing to call /external-accounts to add the account. Useful + * for one-off payments to some destination. See the external accounts endpoints for test + * values in sandbox mode. + */ + fun visitExternalAccountDetails(externalAccountDetails: ExternalAccountDetails): T + + /** + * Maps an unknown variant of [QuoteDestinationOneOf] to a value of type [T]. + * + * An instance of [QuoteDestinationOneOf] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown QuoteDestinationOneOf: $json") + } + } + + internal class Deserializer : + BaseDeserializer(QuoteDestinationOneOf::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): QuoteDestinationOneOf { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + QuoteDestinationOneOf(account = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + QuoteDestinationOneOf(umaAddress = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + QuoteDestinationOneOf(externalAccountDetails = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> QuoteDestinationOneOf(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(QuoteDestinationOneOf::class) { + + override fun serialize( + value: QuoteDestinationOneOf, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.account != null -> generator.writeObject(value.account) + value.umaAddress != null -> generator.writeObject(value.umaAddress) + value.externalAccountDetails != null -> + generator.writeObject(value.externalAccountDetails) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid QuoteDestinationOneOf") + } + } + } + + /** Destination account details */ + class Account + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountId: JsonField, + private val destinationType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of(), + @JsonProperty("destinationType") + @ExcludeMissing + destinationType: JsonField = JsonMissing.of(), + ) : this(accountId, destinationType, mutableMapOf()) + + fun toBaseDestination(): BaseDestination = BaseDestination.builder().build() + + /** + * Destination account identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destinationType(): DestinationType = destinationType.getRequired("destinationType") + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + /** + * Returns the raw JSON value of [destinationType]. + * + * Unlike [destinationType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("destinationType") + @ExcludeMissing + fun _destinationType(): JsonField = destinationType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Account]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .destinationType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Account]. */ + class Builder internal constructor() { + + private var accountId: JsonField? = null + private var destinationType: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(account: Account) = apply { + accountId = account.accountId + destinationType = account.destinationType + additionalProperties = account.additionalProperties.toMutableMap() + } + + /** Destination account identifier */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun destinationType(destinationType: DestinationType) = + destinationType(JsonField.of(destinationType)) + + /** + * Sets [Builder.destinationType] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationType] with a well-typed [DestinationType] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun destinationType(destinationType: JsonField) = apply { + this.destinationType = destinationType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Account]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .destinationType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Account = + Account( + checkRequired("accountId", accountId), + checkRequired("destinationType", destinationType), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Account = apply { + if (validated) { + return@apply + } + + accountId() + destinationType().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountId.asKnown() == null) 0 else 1) + + (destinationType.asKnown()?.validity() ?: 0) + + class DestinationType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val ACCOUNT = of("ACCOUNT") + + fun of(value: String) = DestinationType(JsonField.of(value)) + } + + /** An enum containing [DestinationType]'s known values. */ + enum class Known { + ACCOUNT + } + + /** + * An enum containing [DestinationType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DestinationType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ACCOUNT, + /** + * An enum member indicating that [DestinationType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ACCOUNT -> Value.ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACCOUNT -> Known.ACCOUNT + else -> throw GridInvalidDataException("Unknown DestinationType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): DestinationType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DestinationType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Account && + accountId == other.accountId && + destinationType == other.destinationType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountId, destinationType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Account{accountId=$accountId, destinationType=$destinationType, additionalProperties=$additionalProperties}" + } + + /** UMA address destination details */ + class UmaAddress + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val destinationType: JsonField, + private val umaAddress: JsonField, + private val counterpartyInformation: JsonField, + private val currency: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("destinationType") + @ExcludeMissing + destinationType: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + @JsonProperty("counterpartyInformation") + @ExcludeMissing + counterpartyInformation: JsonField = JsonMissing.of(), + @JsonProperty("currency") @ExcludeMissing currency: JsonField = JsonMissing.of(), + ) : this(destinationType, umaAddress, counterpartyInformation, currency, mutableMapOf()) + + fun toBaseDestination(): BaseDestination = BaseDestination.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destinationType(): DestinationType = destinationType.getRequired("destinationType") + + /** + * UMA address of the recipient + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * Information about the recipient, as required by the platform in their configuration. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun counterpartyInformation(): CounterpartyInformation? = + counterpartyInformation.getNullable("counterpartyInformation") + + /** + * Currency code for the destination. See + * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) + * for the full list of supported fiat and crypto currencies. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * Returns the raw JSON value of [destinationType]. + * + * Unlike [destinationType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("destinationType") + @ExcludeMissing + fun _destinationType(): JsonField = destinationType + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + /** + * Returns the raw JSON value of [counterpartyInformation]. + * + * Unlike [counterpartyInformation], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("counterpartyInformation") + @ExcludeMissing + fun _counterpartyInformation(): JsonField = counterpartyInformation + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaAddress]. + * + * The following fields are required: + * ```kotlin + * .destinationType() + * .umaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaAddress]. */ + class Builder internal constructor() { + + private var destinationType: JsonField? = null + private var umaAddress: JsonField? = null + private var counterpartyInformation: JsonField = + JsonMissing.of() + private var currency: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(umaAddress: UmaAddress) = apply { + destinationType = umaAddress.destinationType + this.umaAddress = umaAddress.umaAddress + counterpartyInformation = umaAddress.counterpartyInformation + currency = umaAddress.currency + additionalProperties = umaAddress.additionalProperties.toMutableMap() + } + + fun destinationType(destinationType: DestinationType) = + destinationType(JsonField.of(destinationType)) + + /** + * Sets [Builder.destinationType] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationType] with a well-typed [DestinationType] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun destinationType(destinationType: JsonField) = apply { + this.destinationType = destinationType + } + + /** UMA address of the recipient */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + /** + * Information about the recipient, as required by the platform in their configuration. + */ + fun counterpartyInformation(counterpartyInformation: CounterpartyInformation) = + counterpartyInformation(JsonField.of(counterpartyInformation)) + + /** + * Sets [Builder.counterpartyInformation] to an arbitrary JSON value. + * + * You should usually call [Builder.counterpartyInformation] with a well-typed + * [CounterpartyInformation] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun counterpartyInformation( + counterpartyInformation: JsonField + ) = apply { this.counterpartyInformation = counterpartyInformation } + + /** + * Currency code for the destination. See + * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) + * for the full list of supported fiat and crypto currencies. + */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UmaAddress]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destinationType() + * .umaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaAddress = + UmaAddress( + checkRequired("destinationType", destinationType), + checkRequired("umaAddress", umaAddress), + counterpartyInformation, + currency, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UmaAddress = apply { + if (validated) { + return@apply + } + + destinationType().validate() + umaAddress() + counterpartyInformation()?.validate() + currency() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (destinationType.asKnown()?.validity() ?: 0) + + (if (umaAddress.asKnown() == null) 0 else 1) + + (counterpartyInformation.asKnown()?.validity() ?: 0) + + (if (currency.asKnown() == null) 0 else 1) + + class DestinationType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val UMA_ADDRESS = of("UMA_ADDRESS") + + fun of(value: String) = DestinationType(JsonField.of(value)) + } + + /** An enum containing [DestinationType]'s known values. */ + enum class Known { + UMA_ADDRESS + } + + /** + * An enum containing [DestinationType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DestinationType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + UMA_ADDRESS, + /** + * An enum member indicating that [DestinationType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + UMA_ADDRESS -> Value.UMA_ADDRESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + UMA_ADDRESS -> Known.UMA_ADDRESS + else -> throw GridInvalidDataException("Unknown DestinationType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): DestinationType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DestinationType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Information about the recipient, as required by the platform in their configuration. */ + class CounterpartyInformation + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [CounterpartyInformation]. + */ + fun builder() = Builder() + } + + /** A builder for [CounterpartyInformation]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(counterpartyInformation: CounterpartyInformation) = apply { + additionalProperties = + counterpartyInformation.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CounterpartyInformation]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CounterpartyInformation = + CounterpartyInformation(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): CounterpartyInformation = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CounterpartyInformation && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CounterpartyInformation{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaAddress && + destinationType == other.destinationType && + umaAddress == other.umaAddress && + counterpartyInformation == other.counterpartyInformation && + currency == other.currency && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + destinationType, + umaAddress, + counterpartyInformation, + currency, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UmaAddress{destinationType=$destinationType, umaAddress=$umaAddress, counterpartyInformation=$counterpartyInformation, currency=$currency, additionalProperties=$additionalProperties}" + } + + /** + * A convenient destination option which adds the external account and creates the quote in one + * step rather than first needing to call /external-accounts to add the account. Useful for + * one-off payments to some destination. See the external accounts endpoints for test values in + * sandbox mode. + */ + class ExternalAccountDetails + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val destinationType: JsonField, + private val externalAccountDetails: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("destinationType") + @ExcludeMissing + destinationType: JsonField = JsonMissing.of(), + @JsonProperty("externalAccountDetails") + @ExcludeMissing + externalAccountDetails: JsonField = JsonMissing.of(), + ) : this(destinationType, externalAccountDetails, mutableMapOf()) + + fun toBaseDestination(): BaseDestination = BaseDestination.builder().build() + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destinationType(): DestinationType = destinationType.getRequired("destinationType") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun externalAccountDetails(): ExternalAccountCreate = + externalAccountDetails.getRequired("externalAccountDetails") + + /** + * Returns the raw JSON value of [destinationType]. + * + * Unlike [destinationType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("destinationType") + @ExcludeMissing + fun _destinationType(): JsonField = destinationType + + /** + * Returns the raw JSON value of [externalAccountDetails]. + * + * Unlike [externalAccountDetails], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("externalAccountDetails") + @ExcludeMissing + fun _externalAccountDetails(): JsonField = externalAccountDetails + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ExternalAccountDetails]. + * + * The following fields are required: + * ```kotlin + * .destinationType() + * .externalAccountDetails() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ExternalAccountDetails]. */ + class Builder internal constructor() { + + private var destinationType: JsonField? = null + private var externalAccountDetails: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(externalAccountDetails: ExternalAccountDetails) = apply { + destinationType = externalAccountDetails.destinationType + this.externalAccountDetails = externalAccountDetails.externalAccountDetails + additionalProperties = externalAccountDetails.additionalProperties.toMutableMap() + } + + fun destinationType(destinationType: DestinationType) = + destinationType(JsonField.of(destinationType)) + + /** + * Sets [Builder.destinationType] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationType] with a well-typed [DestinationType] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun destinationType(destinationType: JsonField) = apply { + this.destinationType = destinationType + } + + fun externalAccountDetails(externalAccountDetails: ExternalAccountCreate) = + externalAccountDetails(JsonField.of(externalAccountDetails)) + + /** + * Sets [Builder.externalAccountDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.externalAccountDetails] with a well-typed + * [ExternalAccountCreate] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun externalAccountDetails(externalAccountDetails: JsonField) = + apply { + this.externalAccountDetails = externalAccountDetails + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ExternalAccountDetails]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destinationType() + * .externalAccountDetails() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ExternalAccountDetails = + ExternalAccountDetails( + checkRequired("destinationType", destinationType), + checkRequired("externalAccountDetails", externalAccountDetails), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ExternalAccountDetails = apply { + if (validated) { + return@apply + } + + destinationType().validate() + externalAccountDetails().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (destinationType.asKnown()?.validity() ?: 0) + + (externalAccountDetails.asKnown()?.validity() ?: 0) + + class DestinationType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val EXTERNAL_ACCOUNT_DETAILS = of("EXTERNAL_ACCOUNT_DETAILS") + + fun of(value: String) = DestinationType(JsonField.of(value)) + } + + /** An enum containing [DestinationType]'s known values. */ + enum class Known { + EXTERNAL_ACCOUNT_DETAILS + } + + /** + * An enum containing [DestinationType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DestinationType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + EXTERNAL_ACCOUNT_DETAILS, + /** + * An enum member indicating that [DestinationType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + EXTERNAL_ACCOUNT_DETAILS -> Value.EXTERNAL_ACCOUNT_DETAILS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + EXTERNAL_ACCOUNT_DETAILS -> Known.EXTERNAL_ACCOUNT_DETAILS + else -> throw GridInvalidDataException("Unknown DestinationType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): DestinationType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DestinationType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ExternalAccountDetails && + destinationType == other.destinationType && + externalAccountDetails == other.externalAccountDetails && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(destinationType, externalAccountDetails, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ExternalAccountDetails{destinationType=$destinationType, externalAccountDetails=$externalAccountDetails, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteExecuteParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteExecuteParams.kt new file mode 100644 index 00000000..d8f467a2 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteExecuteParams.kt @@ -0,0 +1,230 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import java.util.Objects + +/** + * Execute a quote by its ID. This endpoint initiates the transfer between the source and + * destination accounts. + * + * This endpoint can only be used for quotes with a `source` which is either an internal account, or + * has direct pull functionality (e.g. ACH pull with an external account). + * + * Once executed, the quote cannot be cancelled and the transfer will be processed. + */ +class QuoteExecuteParams +private constructor( + private val quoteId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun quoteId(): String? = quoteId + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): QuoteExecuteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [QuoteExecuteParams]. */ + fun builder() = Builder() + } + + /** A builder for [QuoteExecuteParams]. */ + class Builder internal constructor() { + + private var quoteId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + internal fun from(quoteExecuteParams: QuoteExecuteParams) = apply { + quoteId = quoteExecuteParams.quoteId + additionalHeaders = quoteExecuteParams.additionalHeaders.toBuilder() + additionalQueryParams = quoteExecuteParams.additionalQueryParams.toBuilder() + additionalBodyProperties = quoteExecuteParams.additionalBodyProperties.toMutableMap() + } + + fun quoteId(quoteId: String?) = apply { this.quoteId = quoteId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [QuoteExecuteParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): QuoteExecuteParams = + QuoteExecuteParams( + quoteId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Map? = additionalBodyProperties.ifEmpty { null } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> quoteId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteExecuteParams && + quoteId == other.quoteId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash(quoteId, additionalHeaders, additionalQueryParams, additionalBodyProperties) + + override fun toString() = + "QuoteExecuteParams{quoteId=$quoteId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPage.kt new file mode 100644 index 00000000..abcfcec5 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPage.kt @@ -0,0 +1,141 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.services.blocking.QuoteService +import java.util.Objects + +/** @see QuoteService.list */ +class QuoteListPage +private constructor( + private val service: QuoteService, + private val params: QuoteListParams, + private val response: QuoteListPageResponse, +) : Page { + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): QuoteListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): QuoteListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): QuoteListParams = params + + /** The response that this page was parsed from. */ + fun response(): QuoteListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [QuoteListPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [QuoteListPage]. */ + class Builder internal constructor() { + + private var service: QuoteService? = null + private var params: QuoteListParams? = null + private var response: QuoteListPageResponse? = null + + internal fun from(quoteListPage: QuoteListPage) = apply { + service = quoteListPage.service + params = quoteListPage.params + response = quoteListPage.response + } + + fun service(service: QuoteService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: QuoteListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: QuoteListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [QuoteListPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): QuoteListPage = + QuoteListPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteListPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = "QuoteListPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageAsync.kt new file mode 100644 index 00000000..ba099391 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageAsync.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.services.async.QuoteServiceAsync +import java.util.Objects + +/** @see QuoteServiceAsync.list */ +class QuoteListPageAsync +private constructor( + private val service: QuoteServiceAsync, + private val params: QuoteListParams, + private val response: QuoteListPageResponse, +) : PageAsync { + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [QuoteListPageResponse], but gracefully handles missing data. + * + * @see QuoteListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): QuoteListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): QuoteListPageAsync = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): QuoteListParams = params + + /** The response that this page was parsed from. */ + fun response(): QuoteListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [QuoteListPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [QuoteListPageAsync]. */ + class Builder internal constructor() { + + private var service: QuoteServiceAsync? = null + private var params: QuoteListParams? = null + private var response: QuoteListPageResponse? = null + + internal fun from(quoteListPageAsync: QuoteListPageAsync) = apply { + service = quoteListPageAsync.service + params = quoteListPageAsync.params + response = quoteListPageAsync.response + } + + fun service(service: QuoteServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: QuoteListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: QuoteListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [QuoteListPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): QuoteListPageAsync = + QuoteListPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteListPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "QuoteListPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageResponse.kt new file mode 100644 index 00000000..3642e50a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListPageResponse.kt @@ -0,0 +1,299 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class QuoteListPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of quotes matching the criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun hasMore(): Boolean = hasMore.getRequired("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of quotes matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [QuoteListPageResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [QuoteListPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField? = null + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(quoteListPageResponse: QuoteListPageResponse) = apply { + data = quoteListPageResponse.data.map { it.toMutableList() } + hasMore = quoteListPageResponse.hasMore + nextCursor = quoteListPageResponse.nextCursor + totalCount = quoteListPageResponse.totalCount + additionalProperties = quoteListPageResponse.additionalProperties.toMutableMap() + } + + /** List of quotes matching the criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [Quote] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: Quote) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of quotes matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [QuoteListPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): QuoteListPageResponse = + QuoteListPageResponse( + checkRequired("data", data).map { it.toImmutable() }, + checkRequired("hasMore", hasMore), + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): QuoteListPageResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteListPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "QuoteListPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListParams.kt new file mode 100644 index 00000000..c50ed1ac --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteListParams.kt @@ -0,0 +1,484 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.util.Objects + +/** + * Retrieve a list of transfer quotes with optional filtering parameters. Returns all quotes that + * match the specified filters. If no filters are provided, returns all quotes (paginated). + */ +class QuoteListParams +private constructor( + private val createdAfter: OffsetDateTime?, + private val createdBefore: OffsetDateTime?, + private val cursor: String?, + private val customerId: String?, + private val limit: Long?, + private val receivingAccountId: String?, + private val receivingUmaAddress: String?, + private val sendingAccountId: String?, + private val sendingUmaAddress: String?, + private val status: Status?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter quotes created after this timestamp (inclusive) */ + fun createdAfter(): OffsetDateTime? = createdAfter + + /** Filter quotes created before this timestamp (inclusive) */ + fun createdBefore(): OffsetDateTime? = createdBefore + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** Filter by sending customer ID */ + fun customerId(): String? = customerId + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Filter by receiving account ID */ + fun receivingAccountId(): String? = receivingAccountId + + /** Filter by receiving UMA address */ + fun receivingUmaAddress(): String? = receivingUmaAddress + + /** Filter by sending account ID */ + fun sendingAccountId(): String? = sendingAccountId + + /** Filter by sending UMA address */ + fun sendingUmaAddress(): String? = sendingUmaAddress + + /** Filter by quote status */ + fun status(): Status? = status + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): QuoteListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [QuoteListParams]. */ + fun builder() = Builder() + } + + /** A builder for [QuoteListParams]. */ + class Builder internal constructor() { + + private var createdAfter: OffsetDateTime? = null + private var createdBefore: OffsetDateTime? = null + private var cursor: String? = null + private var customerId: String? = null + private var limit: Long? = null + private var receivingAccountId: String? = null + private var receivingUmaAddress: String? = null + private var sendingAccountId: String? = null + private var sendingUmaAddress: String? = null + private var status: Status? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(quoteListParams: QuoteListParams) = apply { + createdAfter = quoteListParams.createdAfter + createdBefore = quoteListParams.createdBefore + cursor = quoteListParams.cursor + customerId = quoteListParams.customerId + limit = quoteListParams.limit + receivingAccountId = quoteListParams.receivingAccountId + receivingUmaAddress = quoteListParams.receivingUmaAddress + sendingAccountId = quoteListParams.sendingAccountId + sendingUmaAddress = quoteListParams.sendingUmaAddress + status = quoteListParams.status + additionalHeaders = quoteListParams.additionalHeaders.toBuilder() + additionalQueryParams = quoteListParams.additionalQueryParams.toBuilder() + } + + /** Filter quotes created after this timestamp (inclusive) */ + fun createdAfter(createdAfter: OffsetDateTime?) = apply { this.createdAfter = createdAfter } + + /** Filter quotes created before this timestamp (inclusive) */ + fun createdBefore(createdBefore: OffsetDateTime?) = apply { + this.createdBefore = createdBefore + } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** Filter by sending customer ID */ + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Filter by receiving account ID */ + fun receivingAccountId(receivingAccountId: String?) = apply { + this.receivingAccountId = receivingAccountId + } + + /** Filter by receiving UMA address */ + fun receivingUmaAddress(receivingUmaAddress: String?) = apply { + this.receivingUmaAddress = receivingUmaAddress + } + + /** Filter by sending account ID */ + fun sendingAccountId(sendingAccountId: String?) = apply { + this.sendingAccountId = sendingAccountId + } + + /** Filter by sending UMA address */ + fun sendingUmaAddress(sendingUmaAddress: String?) = apply { + this.sendingUmaAddress = sendingUmaAddress + } + + /** Filter by quote status */ + fun status(status: Status?) = apply { this.status = status } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [QuoteListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): QuoteListParams = + QuoteListParams( + createdAfter, + createdBefore, + cursor, + customerId, + limit, + receivingAccountId, + receivingUmaAddress, + sendingAccountId, + sendingUmaAddress, + status, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + createdAfter?.let { + put("createdAfter", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + createdBefore?.let { + put("createdBefore", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + cursor?.let { put("cursor", it) } + customerId?.let { put("customerId", it) } + limit?.let { put("limit", it.toString()) } + receivingAccountId?.let { put("receivingAccountId", it) } + receivingUmaAddress?.let { put("receivingUmaAddress", it) } + sendingAccountId?.let { put("sendingAccountId", it) } + sendingUmaAddress?.let { put("sendingUmaAddress", it) } + status?.let { put("status", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + /** Filter by quote status */ + class Status @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val PENDING = of("PENDING") + + val PROCESSING = of("PROCESSING") + + val COMPLETED = of("COMPLETED") + + val FAILED = of("FAILED") + + val EXPIRED = of("EXPIRED") + + fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + PENDING, + PROCESSING, + COMPLETED, + FAILED, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + PENDING, + PROCESSING, + COMPLETED, + FAILED, + EXPIRED, + /** An enum member indicating that [Status] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + PENDING -> Value.PENDING + PROCESSING -> Value.PROCESSING + COMPLETED -> Value.COMPLETED + FAILED -> Value.FAILED + EXPIRED -> Value.EXPIRED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + PENDING -> Known.PENDING + PROCESSING -> Known.PROCESSING + COMPLETED -> Known.COMPLETED + FAILED -> Known.FAILED + EXPIRED -> Known.EXPIRED + else -> throw GridInvalidDataException("Unknown Status: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteListParams && + createdAfter == other.createdAfter && + createdBefore == other.createdBefore && + cursor == other.cursor && + customerId == other.customerId && + limit == other.limit && + receivingAccountId == other.receivingAccountId && + receivingUmaAddress == other.receivingUmaAddress && + sendingAccountId == other.sendingAccountId && + sendingUmaAddress == other.sendingUmaAddress && + status == other.status && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + createdAfter, + createdBefore, + cursor, + customerId, + limit, + receivingAccountId, + receivingUmaAddress, + sendingAccountId, + sendingUmaAddress, + status, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "QuoteListParams{createdAfter=$createdAfter, createdBefore=$createdBefore, cursor=$cursor, customerId=$customerId, limit=$limit, receivingAccountId=$receivingAccountId, receivingUmaAddress=$receivingUmaAddress, sendingAccountId=$sendingAccountId, sendingUmaAddress=$sendingUmaAddress, status=$status, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteRetrieveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteRetrieveParams.kt new file mode 100644 index 00000000..ca7ff266 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteRetrieveParams.kt @@ -0,0 +1,186 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Retrieve a quote by its ID. If the quote has been settled, it will include the transaction ID. + * This allows clients to track the full lifecycle of a payment from quote creation to settlement. + */ +class QuoteRetrieveParams +private constructor( + private val quoteId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun quoteId(): String? = quoteId + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): QuoteRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [QuoteRetrieveParams]. */ + fun builder() = Builder() + } + + /** A builder for [QuoteRetrieveParams]. */ + class Builder internal constructor() { + + private var quoteId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(quoteRetrieveParams: QuoteRetrieveParams) = apply { + quoteId = quoteRetrieveParams.quoteId + additionalHeaders = quoteRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = quoteRetrieveParams.additionalQueryParams.toBuilder() + } + + fun quoteId(quoteId: String?) = apply { this.quoteId = quoteId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [QuoteRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): QuoteRetrieveParams = + QuoteRetrieveParams(quoteId, additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> quoteId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteRetrieveParams && + quoteId == other.quoteId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(quoteId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "QuoteRetrieveParams{quoteId=$quoteId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteSourceOneOf.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteSourceOneOf.kt new file mode 100644 index 00000000..940da49b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/quotes/QuoteSourceOneOf.kt @@ -0,0 +1,991 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** Source account details */ +@JsonDeserialize(using = QuoteSourceOneOf.Deserializer::class) +@JsonSerialize(using = QuoteSourceOneOf.Serializer::class) +class QuoteSourceOneOf +private constructor( + private val account: Account? = null, + private val realtimeFunding: RealtimeFunding? = null, + private val _json: JsonValue? = null, +) { + + /** Source account details */ + fun account(): Account? = account + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). + * This will require manual just-in-time funding using `paymentInstructions` in the response. + * Because quotes expire quickly, this option is only valid for instant payment methods. Do not + * try to fund a quote with a non-instant payment method (ACH, etc.). + */ + fun realtimeFunding(): RealtimeFunding? = realtimeFunding + + fun isAccount(): Boolean = account != null + + fun isRealtimeFunding(): Boolean = realtimeFunding != null + + /** Source account details */ + fun asAccount(): Account = account.getOrThrow("account") + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). + * This will require manual just-in-time funding using `paymentInstructions` in the response. + * Because quotes expire quickly, this option is only valid for instant payment methods. Do not + * try to fund a quote with a non-instant payment method (ACH, etc.). + */ + fun asRealtimeFunding(): RealtimeFunding = realtimeFunding.getOrThrow("realtimeFunding") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + account != null -> visitor.visitAccount(account) + realtimeFunding != null -> visitor.visitRealtimeFunding(realtimeFunding) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): QuoteSourceOneOf = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAccount(account: Account) { + account.validate() + } + + override fun visitRealtimeFunding(realtimeFunding: RealtimeFunding) { + realtimeFunding.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAccount(account: Account) = account.validity() + + override fun visitRealtimeFunding(realtimeFunding: RealtimeFunding) = + realtimeFunding.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QuoteSourceOneOf && + account == other.account && + realtimeFunding == other.realtimeFunding + } + + override fun hashCode(): Int = Objects.hash(account, realtimeFunding) + + override fun toString(): String = + when { + account != null -> "QuoteSourceOneOf{account=$account}" + realtimeFunding != null -> "QuoteSourceOneOf{realtimeFunding=$realtimeFunding}" + _json != null -> "QuoteSourceOneOf{_unknown=$_json}" + else -> throw IllegalStateException("Invalid QuoteSourceOneOf") + } + + companion object { + + /** Source account details */ + fun ofAccount(account: Account) = QuoteSourceOneOf(account = account) + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, + * etc.). This will require manual just-in-time funding using `paymentInstructions` in the + * response. Because quotes expire quickly, this option is only valid for instant payment + * methods. Do not try to fund a quote with a non-instant payment method (ACH, etc.). + */ + fun ofRealtimeFunding(realtimeFunding: RealtimeFunding) = + QuoteSourceOneOf(realtimeFunding = realtimeFunding) + } + + /** + * An interface that defines how to map each variant of [QuoteSourceOneOf] to a value of type + * [T]. + */ + interface Visitor { + + /** Source account details */ + fun visitAccount(account: Account): T + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, + * etc.). This will require manual just-in-time funding using `paymentInstructions` in the + * response. Because quotes expire quickly, this option is only valid for instant payment + * methods. Do not try to fund a quote with a non-instant payment method (ACH, etc.). + */ + fun visitRealtimeFunding(realtimeFunding: RealtimeFunding): T + + /** + * Maps an unknown variant of [QuoteSourceOneOf] to a value of type [T]. + * + * An instance of [QuoteSourceOneOf] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is unaware + * of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown QuoteSourceOneOf: $json") + } + } + + internal class Deserializer : BaseDeserializer(QuoteSourceOneOf::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): QuoteSourceOneOf { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + QuoteSourceOneOf(account = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + QuoteSourceOneOf(realtimeFunding = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> QuoteSourceOneOf(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(QuoteSourceOneOf::class) { + + override fun serialize( + value: QuoteSourceOneOf, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.account != null -> generator.writeObject(value.account) + value.realtimeFunding != null -> generator.writeObject(value.realtimeFunding) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid QuoteSourceOneOf") + } + } + } + + /** Source account details */ + class Account + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountId: JsonField, + private val sourceType: JsonField, + private val customerId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of(), + @JsonProperty("sourceType") + @ExcludeMissing + sourceType: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + ) : this(accountId, sourceType, customerId, mutableMapOf()) + + fun toBaseQuoteSource(): BaseQuoteSource = BaseQuoteSource.builder().build() + + /** + * Source account identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun sourceType(): SourceType = sourceType.getRequired("sourceType") + + /** + * Required when funding from an FBO account to identify the customer on whose behalf the + * transaction is being initiated. Otherwise, will default to the customerId of the account + * owner. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + /** + * Returns the raw JSON value of [sourceType]. + * + * Unlike [sourceType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sourceType") + @ExcludeMissing + fun _sourceType(): JsonField = sourceType + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") + @ExcludeMissing + fun _customerId(): JsonField = customerId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Account]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .sourceType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Account]. */ + class Builder internal constructor() { + + private var accountId: JsonField? = null + private var sourceType: JsonField? = null + private var customerId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(account: Account) = apply { + accountId = account.accountId + sourceType = account.sourceType + customerId = account.customerId + additionalProperties = account.additionalProperties.toMutableMap() + } + + /** Source account identifier */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun sourceType(sourceType: SourceType) = sourceType(JsonField.of(sourceType)) + + /** + * Sets [Builder.sourceType] to an arbitrary JSON value. + * + * You should usually call [Builder.sourceType] with a well-typed [SourceType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sourceType(sourceType: JsonField) = apply { + this.sourceType = sourceType + } + + /** + * Required when funding from an FBO account to identify the customer on whose behalf + * the transaction is being initiated. Otherwise, will default to the customerId of the + * account owner. + */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Account]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .sourceType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Account = + Account( + checkRequired("accountId", accountId), + checkRequired("sourceType", sourceType), + customerId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Account = apply { + if (validated) { + return@apply + } + + accountId() + sourceType().validate() + customerId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (accountId.asKnown() == null) 0 else 1) + + (sourceType.asKnown()?.validity() ?: 0) + + (if (customerId.asKnown() == null) 0 else 1) + + class SourceType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val ACCOUNT = of("ACCOUNT") + + fun of(value: String) = SourceType(JsonField.of(value)) + } + + /** An enum containing [SourceType]'s known values. */ + enum class Known { + ACCOUNT + } + + /** + * An enum containing [SourceType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SourceType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ACCOUNT, + /** + * An enum member indicating that [SourceType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ACCOUNT -> Value.ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACCOUNT -> Known.ACCOUNT + else -> throw GridInvalidDataException("Unknown SourceType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): SourceType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SourceType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Account && + accountId == other.accountId && + sourceType == other.sourceType && + customerId == other.customerId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accountId, sourceType, customerId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Account{accountId=$accountId, sourceType=$sourceType, customerId=$customerId, additionalProperties=$additionalProperties}" + } + + /** + * Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). + * This will require manual just-in-time funding using `paymentInstructions` in the response. + * Because quotes expire quickly, this option is only valid for instant payment methods. Do not + * try to fund a quote with a non-instant payment method (ACH, etc.). + */ + class RealtimeFunding + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currency: JsonField, + private val sourceType: JsonField, + private val customerId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("sourceType") + @ExcludeMissing + sourceType: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + ) : this(currency, sourceType, customerId, mutableMapOf()) + + fun toBaseQuoteSource(): BaseQuoteSource = BaseQuoteSource.builder().build() + + /** + * Currency code for the funding source. See + * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) + * for the full list of supported fiat and crypto currencies. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun currency(): String = currency.getRequired("currency") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun sourceType(): SourceType = sourceType.getRequired("sourceType") + + /** + * Source customer ID. If this transaction is being initiated on behalf of a customer, this + * is required. If customerId is not provided, the quote will be created on behalf of the + * platform itself. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [sourceType]. + * + * Unlike [sourceType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sourceType") + @ExcludeMissing + fun _sourceType(): JsonField = sourceType + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") + @ExcludeMissing + fun _customerId(): JsonField = customerId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RealtimeFunding]. + * + * The following fields are required: + * ```kotlin + * .currency() + * .sourceType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [RealtimeFunding]. */ + class Builder internal constructor() { + + private var currency: JsonField? = null + private var sourceType: JsonField? = null + private var customerId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(realtimeFunding: RealtimeFunding) = apply { + currency = realtimeFunding.currency + sourceType = realtimeFunding.sourceType + customerId = realtimeFunding.customerId + additionalProperties = realtimeFunding.additionalProperties.toMutableMap() + } + + /** + * Currency code for the funding source. See + * [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) + * for the full list of supported fiat and crypto currencies. + */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun sourceType(sourceType: SourceType) = sourceType(JsonField.of(sourceType)) + + /** + * Sets [Builder.sourceType] to an arbitrary JSON value. + * + * You should usually call [Builder.sourceType] with a well-typed [SourceType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sourceType(sourceType: JsonField) = apply { + this.sourceType = sourceType + } + + /** + * Source customer ID. If this transaction is being initiated on behalf of a customer, + * this is required. If customerId is not provided, the quote will be created on behalf + * of the platform itself. + */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [RealtimeFunding]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .currency() + * .sourceType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RealtimeFunding = + RealtimeFunding( + checkRequired("currency", currency), + checkRequired("sourceType", sourceType), + customerId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): RealtimeFunding = apply { + if (validated) { + return@apply + } + + currency() + sourceType().validate() + customerId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currency.asKnown() == null) 0 else 1) + + (sourceType.asKnown()?.validity() ?: 0) + + (if (customerId.asKnown() == null) 0 else 1) + + class SourceType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val REALTIME_FUNDING = of("REALTIME_FUNDING") + + fun of(value: String) = SourceType(JsonField.of(value)) + } + + /** An enum containing [SourceType]'s known values. */ + enum class Known { + REALTIME_FUNDING + } + + /** + * An enum containing [SourceType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SourceType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + REALTIME_FUNDING, + /** + * An enum member indicating that [SourceType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + REALTIME_FUNDING -> Value.REALTIME_FUNDING + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + REALTIME_FUNDING -> Known.REALTIME_FUNDING + else -> throw GridInvalidDataException("Unknown SourceType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): SourceType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SourceType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is RealtimeFunding && + currency == other.currency && + sourceType == other.sourceType && + customerId == other.customerId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currency, sourceType, customerId, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "RealtimeFunding{currency=$currency, sourceType=$sourceType, customerId=$customerId, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinition.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinition.kt new file mode 100644 index 00000000..60e68ba6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinition.kt @@ -0,0 +1,216 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.config.CustomerInfoFieldName +import java.util.Collections +import java.util.Objects + +class CounterpartyFieldDefinition +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val mandatory: JsonField, + private val name: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("mandatory") @ExcludeMissing mandatory: JsonField = JsonMissing.of(), + @JsonProperty("name") + @ExcludeMissing + name: JsonField = JsonMissing.of(), + ) : this(mandatory, name, mutableMapOf()) + + /** + * Whether the field is mandatory + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun mandatory(): Boolean = mandatory.getRequired("mandatory") + + /** + * Name of a type of field containing info about a platform's customer or counterparty customer. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): CustomerInfoFieldName = name.getRequired("name") + + /** + * Returns the raw JSON value of [mandatory]. + * + * Unlike [mandatory], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mandatory") @ExcludeMissing fun _mandatory(): JsonField = mandatory + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CounterpartyFieldDefinition]. + * + * The following fields are required: + * ```kotlin + * .mandatory() + * .name() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [CounterpartyFieldDefinition]. */ + class Builder internal constructor() { + + private var mandatory: JsonField? = null + private var name: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(counterpartyFieldDefinition: CounterpartyFieldDefinition) = apply { + mandatory = counterpartyFieldDefinition.mandatory + name = counterpartyFieldDefinition.name + additionalProperties = counterpartyFieldDefinition.additionalProperties.toMutableMap() + } + + /** Whether the field is mandatory */ + fun mandatory(mandatory: Boolean) = mandatory(JsonField.of(mandatory)) + + /** + * Sets [Builder.mandatory] to an arbitrary JSON value. + * + * You should usually call [Builder.mandatory] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun mandatory(mandatory: JsonField) = apply { this.mandatory = mandatory } + + /** + * Name of a type of field containing info about a platform's customer or counterparty + * customer. + */ + fun name(name: CustomerInfoFieldName) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [CustomerInfoFieldName] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CounterpartyFieldDefinition]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .mandatory() + * .name() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CounterpartyFieldDefinition = + CounterpartyFieldDefinition( + checkRequired("mandatory", mandatory), + checkRequired("name", name), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): CounterpartyFieldDefinition = apply { + if (validated) { + return@apply + } + + mandatory() + name().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (mandatory.asKnown() == null) 0 else 1) + (name.asKnown()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CounterpartyFieldDefinition && + mandatory == other.mandatory && + name == other.name && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(mandatory, name, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CounterpartyFieldDefinition{mandatory=$mandatory, name=$name, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/LookupResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/LookupResponse.kt new file mode 100644 index 00000000..6489ae58 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/LookupResponse.kt @@ -0,0 +1,584 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.quotes.Currency +import java.util.Collections +import java.util.Objects + +class LookupResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val lookupId: JsonField, + private val supportedCurrencies: JsonField>, + private val requiredPayerDataFields: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("lookupId") @ExcludeMissing lookupId: JsonField = JsonMissing.of(), + @JsonProperty("supportedCurrencies") + @ExcludeMissing + supportedCurrencies: JsonField> = JsonMissing.of(), + @JsonProperty("requiredPayerDataFields") + @ExcludeMissing + requiredPayerDataFields: JsonField> = JsonMissing.of(), + ) : this(lookupId, supportedCurrencies, requiredPayerDataFields, mutableMapOf()) + + /** + * Unique identifier for the lookup. Needed in the subsequent create quote request. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun lookupId(): String = lookupId.getRequired("lookupId") + + /** + * List of currencies supported by the receiving account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun supportedCurrencies(): List = + supportedCurrencies.getRequired("supportedCurrencies") + + /** + * Fields required by the receiving institution about the payer before payment can be completed + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun requiredPayerDataFields(): List? = + requiredPayerDataFields.getNullable("requiredPayerDataFields") + + /** + * Returns the raw JSON value of [lookupId]. + * + * Unlike [lookupId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lookupId") @ExcludeMissing fun _lookupId(): JsonField = lookupId + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("supportedCurrencies") + @ExcludeMissing + fun _supportedCurrencies(): JsonField> = supportedCurrencies + + /** + * Returns the raw JSON value of [requiredPayerDataFields]. + * + * Unlike [requiredPayerDataFields], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("requiredPayerDataFields") + @ExcludeMissing + fun _requiredPayerDataFields(): JsonField> = + requiredPayerDataFields + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [LookupResponse]. + * + * The following fields are required: + * ```kotlin + * .lookupId() + * .supportedCurrencies() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [LookupResponse]. */ + class Builder internal constructor() { + + private var lookupId: JsonField? = null + private var supportedCurrencies: JsonField>? = null + private var requiredPayerDataFields: JsonField>? = + null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(lookupResponse: LookupResponse) = apply { + lookupId = lookupResponse.lookupId + supportedCurrencies = lookupResponse.supportedCurrencies.map { it.toMutableList() } + requiredPayerDataFields = + lookupResponse.requiredPayerDataFields.map { it.toMutableList() } + additionalProperties = lookupResponse.additionalProperties.toMutableMap() + } + + /** Unique identifier for the lookup. Needed in the subsequent create quote request. */ + fun lookupId(lookupId: String) = lookupId(JsonField.of(lookupId)) + + /** + * Sets [Builder.lookupId] to an arbitrary JSON value. + * + * You should usually call [Builder.lookupId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun lookupId(lookupId: JsonField) = apply { this.lookupId = lookupId } + + /** List of currencies supported by the receiving account */ + fun supportedCurrencies(supportedCurrencies: List) = + supportedCurrencies(JsonField.of(supportedCurrencies)) + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun supportedCurrencies(supportedCurrencies: JsonField>) = apply { + this.supportedCurrencies = supportedCurrencies.map { it.toMutableList() } + } + + /** + * Adds a single [SupportedCurrency] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: SupportedCurrency) = apply { + supportedCurrencies = + (supportedCurrencies ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedCurrencies", it).add(supportedCurrency) + } + } + + /** + * Fields required by the receiving institution about the payer before payment can be + * completed + */ + fun requiredPayerDataFields(requiredPayerDataFields: List) = + requiredPayerDataFields(JsonField.of(requiredPayerDataFields)) + + /** + * Sets [Builder.requiredPayerDataFields] to an arbitrary JSON value. + * + * You should usually call [Builder.requiredPayerDataFields] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun requiredPayerDataFields( + requiredPayerDataFields: JsonField> + ) = apply { + this.requiredPayerDataFields = requiredPayerDataFields.map { it.toMutableList() } + } + + /** + * Adds a single [CounterpartyFieldDefinition] to [requiredPayerDataFields]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRequiredPayerDataField(requiredPayerDataField: CounterpartyFieldDefinition) = apply { + requiredPayerDataFields = + (requiredPayerDataFields ?: JsonField.of(mutableListOf())).also { + checkKnown("requiredPayerDataFields", it).add(requiredPayerDataField) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [LookupResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .lookupId() + * .supportedCurrencies() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): LookupResponse = + LookupResponse( + checkRequired("lookupId", lookupId), + checkRequired("supportedCurrencies", supportedCurrencies).map { it.toImmutable() }, + (requiredPayerDataFields ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): LookupResponse = apply { + if (validated) { + return@apply + } + + lookupId() + supportedCurrencies().forEach { it.validate() } + requiredPayerDataFields()?.forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (lookupId.asKnown() == null) 0 else 1) + + (supportedCurrencies.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (requiredPayerDataFields.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + class SupportedCurrency + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currency: JsonField, + private val estimatedExchangeRate: JsonField, + private val max: JsonField, + private val min: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("estimatedExchangeRate") + @ExcludeMissing + estimatedExchangeRate: JsonField = JsonMissing.of(), + @JsonProperty("max") @ExcludeMissing max: JsonField = JsonMissing.of(), + @JsonProperty("min") @ExcludeMissing min: JsonField = JsonMissing.of(), + ) : this(currency, estimatedExchangeRate, max, min, mutableMapOf()) + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun currency(): Currency = currency.getRequired("currency") + + /** + * An estimated exchange rate from the sender's currency to this currency. This is not a + * locked rate and is subject to change when calling the quotes endpoint. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun estimatedExchangeRate(): Double = + estimatedExchangeRate.getRequired("estimatedExchangeRate") + + /** + * The maximum amount that can be received in this currency. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun max(): Long = max.getRequired("max") + + /** + * The minimum amount that can be received in this currency. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun min(): Long = min.getRequired("min") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [estimatedExchangeRate]. + * + * Unlike [estimatedExchangeRate], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("estimatedExchangeRate") + @ExcludeMissing + fun _estimatedExchangeRate(): JsonField = estimatedExchangeRate + + /** + * Returns the raw JSON value of [max]. + * + * Unlike [max], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("max") @ExcludeMissing fun _max(): JsonField = max + + /** + * Returns the raw JSON value of [min]. + * + * Unlike [min], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("min") @ExcludeMissing fun _min(): JsonField = min + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SupportedCurrency]. + * + * The following fields are required: + * ```kotlin + * .currency() + * .estimatedExchangeRate() + * .max() + * .min() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SupportedCurrency]. */ + class Builder internal constructor() { + + private var currency: JsonField? = null + private var estimatedExchangeRate: JsonField? = null + private var max: JsonField? = null + private var min: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(supportedCurrency: SupportedCurrency) = apply { + currency = supportedCurrency.currency + estimatedExchangeRate = supportedCurrency.estimatedExchangeRate + max = supportedCurrency.max + min = supportedCurrency.min + additionalProperties = supportedCurrency.additionalProperties.toMutableMap() + } + + fun currency(currency: Currency) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [Currency] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + /** + * An estimated exchange rate from the sender's currency to this currency. This is not a + * locked rate and is subject to change when calling the quotes endpoint. + */ + fun estimatedExchangeRate(estimatedExchangeRate: Double) = + estimatedExchangeRate(JsonField.of(estimatedExchangeRate)) + + /** + * Sets [Builder.estimatedExchangeRate] to an arbitrary JSON value. + * + * You should usually call [Builder.estimatedExchangeRate] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun estimatedExchangeRate(estimatedExchangeRate: JsonField) = apply { + this.estimatedExchangeRate = estimatedExchangeRate + } + + /** The maximum amount that can be received in this currency. */ + fun max(max: Long) = max(JsonField.of(max)) + + /** + * Sets [Builder.max] to an arbitrary JSON value. + * + * You should usually call [Builder.max] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun max(max: JsonField) = apply { this.max = max } + + /** The minimum amount that can be received in this currency. */ + fun min(min: Long) = min(JsonField.of(min)) + + /** + * Sets [Builder.min] to an arbitrary JSON value. + * + * You should usually call [Builder.min] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun min(min: JsonField) = apply { this.min = min } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SupportedCurrency]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .currency() + * .estimatedExchangeRate() + * .max() + * .min() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SupportedCurrency = + SupportedCurrency( + checkRequired("currency", currency), + checkRequired("estimatedExchangeRate", estimatedExchangeRate), + checkRequired("max", max), + checkRequired("min", min), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SupportedCurrency = apply { + if (validated) { + return@apply + } + + currency().validate() + estimatedExchangeRate() + max() + min() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (currency.asKnown()?.validity() ?: 0) + + (if (estimatedExchangeRate.asKnown() == null) 0 else 1) + + (if (max.asKnown() == null) 0 else 1) + + (if (min.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SupportedCurrency && + currency == other.currency && + estimatedExchangeRate == other.estimatedExchangeRate && + max == other.max && + min == other.min && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currency, estimatedExchangeRate, max, min, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SupportedCurrency{currency=$currency, estimatedExchangeRate=$estimatedExchangeRate, max=$max, min=$min, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LookupResponse && + lookupId == other.lookupId && + supportedCurrencies == other.supportedCurrencies && + requiredPayerDataFields == other.requiredPayerDataFields && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(lookupId, supportedCurrencies, requiredPayerDataFields, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "LookupResponse{lookupId=$lookupId, supportedCurrencies=$supportedCurrencies, requiredPayerDataFields=$requiredPayerDataFields, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParams.kt new file mode 100644 index 00000000..64d1fe02 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParams.kt @@ -0,0 +1,235 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Lookup an external account by ID to determine supported currencies and exchange rates. This + * endpoint helps platforms determine what currencies they can send to a given external account, + * along with the current estimated exchange rates and minimum and maximum amounts that can be sent. + */ +class ReceiverLookupExternalAccountParams +private constructor( + private val accountId: String?, + private val customerId: String?, + private val senderUmaAddress: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun accountId(): String? = accountId + + /** System ID of the sender (optional if senderUmaAddress is provided) */ + fun customerId(): String? = customerId + + /** UMA address of the sender (optional if customerId is provided) */ + fun senderUmaAddress(): String? = senderUmaAddress + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ReceiverLookupExternalAccountParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of + * [ReceiverLookupExternalAccountParams]. + */ + fun builder() = Builder() + } + + /** A builder for [ReceiverLookupExternalAccountParams]. */ + class Builder internal constructor() { + + private var accountId: String? = null + private var customerId: String? = null + private var senderUmaAddress: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from( + receiverLookupExternalAccountParams: ReceiverLookupExternalAccountParams + ) = apply { + accountId = receiverLookupExternalAccountParams.accountId + customerId = receiverLookupExternalAccountParams.customerId + senderUmaAddress = receiverLookupExternalAccountParams.senderUmaAddress + additionalHeaders = receiverLookupExternalAccountParams.additionalHeaders.toBuilder() + additionalQueryParams = + receiverLookupExternalAccountParams.additionalQueryParams.toBuilder() + } + + fun accountId(accountId: String?) = apply { this.accountId = accountId } + + /** System ID of the sender (optional if senderUmaAddress is provided) */ + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + /** UMA address of the sender (optional if customerId is provided) */ + fun senderUmaAddress(senderUmaAddress: String?) = apply { + this.senderUmaAddress = senderUmaAddress + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ReceiverLookupExternalAccountParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ReceiverLookupExternalAccountParams = + ReceiverLookupExternalAccountParams( + accountId, + customerId, + senderUmaAddress, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> accountId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + customerId?.let { put("customerId", it) } + senderUmaAddress?.let { put("senderUmaAddress", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ReceiverLookupExternalAccountParams && + accountId == other.accountId && + customerId == other.customerId && + senderUmaAddress == other.senderUmaAddress && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + accountId, + customerId, + senderUmaAddress, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "ReceiverLookupExternalAccountParams{accountId=$accountId, customerId=$customerId, senderUmaAddress=$senderUmaAddress, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponse.kt new file mode 100644 index 00000000..0f8c0ab8 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponse.kt @@ -0,0 +1,358 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class ReceiverLookupExternalAccountResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val lookupId: JsonField, + private val supportedCurrencies: JsonField>, + private val requiredPayerDataFields: JsonField>, + private val accountId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("lookupId") @ExcludeMissing lookupId: JsonField = JsonMissing.of(), + @JsonProperty("supportedCurrencies") + @ExcludeMissing + supportedCurrencies: JsonField> = JsonMissing.of(), + @JsonProperty("requiredPayerDataFields") + @ExcludeMissing + requiredPayerDataFields: JsonField> = JsonMissing.of(), + @JsonProperty("accountId") @ExcludeMissing accountId: JsonField = JsonMissing.of(), + ) : this(lookupId, supportedCurrencies, requiredPayerDataFields, accountId, mutableMapOf()) + + fun toLookupResponse(): LookupResponse = + LookupResponse.builder() + .lookupId(lookupId) + .supportedCurrencies(supportedCurrencies) + .requiredPayerDataFields(requiredPayerDataFields) + .build() + + /** + * Unique identifier for the lookup. Needed in the subsequent create quote request. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun lookupId(): String = lookupId.getRequired("lookupId") + + /** + * List of currencies supported by the receiving account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun supportedCurrencies(): List = + supportedCurrencies.getRequired("supportedCurrencies") + + /** + * Fields required by the receiving institution about the payer before payment can be completed + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun requiredPayerDataFields(): List? = + requiredPayerDataFields.getNullable("requiredPayerDataFields") + + /** + * The external account ID that was looked up + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * Returns the raw JSON value of [lookupId]. + * + * Unlike [lookupId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lookupId") @ExcludeMissing fun _lookupId(): JsonField = lookupId + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("supportedCurrencies") + @ExcludeMissing + fun _supportedCurrencies(): JsonField> = + supportedCurrencies + + /** + * Returns the raw JSON value of [requiredPayerDataFields]. + * + * Unlike [requiredPayerDataFields], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("requiredPayerDataFields") + @ExcludeMissing + fun _requiredPayerDataFields(): JsonField> = + requiredPayerDataFields + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [ReceiverLookupExternalAccountResponse]. + * + * The following fields are required: + * ```kotlin + * .lookupId() + * .supportedCurrencies() + * .accountId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ReceiverLookupExternalAccountResponse]. */ + class Builder internal constructor() { + + private var lookupId: JsonField? = null + private var supportedCurrencies: JsonField>? = + null + private var requiredPayerDataFields: JsonField>? = + null + private var accountId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from( + receiverLookupExternalAccountResponse: ReceiverLookupExternalAccountResponse + ) = apply { + lookupId = receiverLookupExternalAccountResponse.lookupId + supportedCurrencies = + receiverLookupExternalAccountResponse.supportedCurrencies.map { it.toMutableList() } + requiredPayerDataFields = + receiverLookupExternalAccountResponse.requiredPayerDataFields.map { + it.toMutableList() + } + accountId = receiverLookupExternalAccountResponse.accountId + additionalProperties = + receiverLookupExternalAccountResponse.additionalProperties.toMutableMap() + } + + /** Unique identifier for the lookup. Needed in the subsequent create quote request. */ + fun lookupId(lookupId: String) = lookupId(JsonField.of(lookupId)) + + /** + * Sets [Builder.lookupId] to an arbitrary JSON value. + * + * You should usually call [Builder.lookupId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun lookupId(lookupId: JsonField) = apply { this.lookupId = lookupId } + + /** List of currencies supported by the receiving account */ + fun supportedCurrencies(supportedCurrencies: List) = + supportedCurrencies(JsonField.of(supportedCurrencies)) + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun supportedCurrencies( + supportedCurrencies: JsonField> + ) = apply { this.supportedCurrencies = supportedCurrencies.map { it.toMutableList() } } + + /** + * Adds a single [LookupResponse.SupportedCurrency] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: LookupResponse.SupportedCurrency) = apply { + supportedCurrencies = + (supportedCurrencies ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedCurrencies", it).add(supportedCurrency) + } + } + + /** + * Fields required by the receiving institution about the payer before payment can be + * completed + */ + fun requiredPayerDataFields(requiredPayerDataFields: List) = + requiredPayerDataFields(JsonField.of(requiredPayerDataFields)) + + /** + * Sets [Builder.requiredPayerDataFields] to an arbitrary JSON value. + * + * You should usually call [Builder.requiredPayerDataFields] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun requiredPayerDataFields( + requiredPayerDataFields: JsonField> + ) = apply { + this.requiredPayerDataFields = requiredPayerDataFields.map { it.toMutableList() } + } + + /** + * Adds a single [CounterpartyFieldDefinition] to [requiredPayerDataFields]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRequiredPayerDataField(requiredPayerDataField: CounterpartyFieldDefinition) = apply { + requiredPayerDataFields = + (requiredPayerDataFields ?: JsonField.of(mutableListOf())).also { + checkKnown("requiredPayerDataFields", it).add(requiredPayerDataField) + } + } + + /** The external account ID that was looked up */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ReceiverLookupExternalAccountResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .lookupId() + * .supportedCurrencies() + * .accountId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ReceiverLookupExternalAccountResponse = + ReceiverLookupExternalAccountResponse( + checkRequired("lookupId", lookupId), + checkRequired("supportedCurrencies", supportedCurrencies).map { it.toImmutable() }, + (requiredPayerDataFields ?: JsonMissing.of()).map { it.toImmutable() }, + checkRequired("accountId", accountId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ReceiverLookupExternalAccountResponse = apply { + if (validated) { + return@apply + } + + lookupId() + supportedCurrencies().forEach { it.validate() } + requiredPayerDataFields()?.forEach { it.validate() } + accountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (lookupId.asKnown() == null) 0 else 1) + + (supportedCurrencies.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (requiredPayerDataFields.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (accountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ReceiverLookupExternalAccountResponse && + lookupId == other.lookupId && + supportedCurrencies == other.supportedCurrencies && + requiredPayerDataFields == other.requiredPayerDataFields && + accountId == other.accountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + lookupId, + supportedCurrencies, + requiredPayerDataFields, + accountId, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ReceiverLookupExternalAccountResponse{lookupId=$lookupId, supportedCurrencies=$supportedCurrencies, requiredPayerDataFields=$requiredPayerDataFields, accountId=$accountId, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParams.kt new file mode 100644 index 00000000..ce06368b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParams.kt @@ -0,0 +1,230 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** + * Lookup a receiving UMA address to determine supported currencies and exchange rates. This + * endpoint helps platforms determine what currencies they can send to a given UMA address. + */ +class ReceiverLookupUmaParams +private constructor( + private val receiverUmaAddress: String?, + private val customerId: String?, + private val senderUmaAddress: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun receiverUmaAddress(): String? = receiverUmaAddress + + /** System ID of the sender (optional if senderUmaAddress is provided) */ + fun customerId(): String? = customerId + + /** UMA address of the sender (optional if customerId is provided) */ + fun senderUmaAddress(): String? = senderUmaAddress + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): ReceiverLookupUmaParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ReceiverLookupUmaParams]. */ + fun builder() = Builder() + } + + /** A builder for [ReceiverLookupUmaParams]. */ + class Builder internal constructor() { + + private var receiverUmaAddress: String? = null + private var customerId: String? = null + private var senderUmaAddress: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(receiverLookupUmaParams: ReceiverLookupUmaParams) = apply { + receiverUmaAddress = receiverLookupUmaParams.receiverUmaAddress + customerId = receiverLookupUmaParams.customerId + senderUmaAddress = receiverLookupUmaParams.senderUmaAddress + additionalHeaders = receiverLookupUmaParams.additionalHeaders.toBuilder() + additionalQueryParams = receiverLookupUmaParams.additionalQueryParams.toBuilder() + } + + fun receiverUmaAddress(receiverUmaAddress: String?) = apply { + this.receiverUmaAddress = receiverUmaAddress + } + + /** System ID of the sender (optional if senderUmaAddress is provided) */ + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + /** UMA address of the sender (optional if customerId is provided) */ + fun senderUmaAddress(senderUmaAddress: String?) = apply { + this.senderUmaAddress = senderUmaAddress + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ReceiverLookupUmaParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ReceiverLookupUmaParams = + ReceiverLookupUmaParams( + receiverUmaAddress, + customerId, + senderUmaAddress, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> receiverUmaAddress ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + customerId?.let { put("customerId", it) } + senderUmaAddress?.let { put("senderUmaAddress", it) } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ReceiverLookupUmaParams && + receiverUmaAddress == other.receiverUmaAddress && + customerId == other.customerId && + senderUmaAddress == other.senderUmaAddress && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + receiverUmaAddress, + customerId, + senderUmaAddress, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "ReceiverLookupUmaParams{receiverUmaAddress=$receiverUmaAddress, customerId=$customerId, senderUmaAddress=$senderUmaAddress, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponse.kt new file mode 100644 index 00000000..e18459ab --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponse.kt @@ -0,0 +1,366 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class ReceiverLookupUmaResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val lookupId: JsonField, + private val supportedCurrencies: JsonField>, + private val requiredPayerDataFields: JsonField>, + private val receiverUmaAddress: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("lookupId") @ExcludeMissing lookupId: JsonField = JsonMissing.of(), + @JsonProperty("supportedCurrencies") + @ExcludeMissing + supportedCurrencies: JsonField> = JsonMissing.of(), + @JsonProperty("requiredPayerDataFields") + @ExcludeMissing + requiredPayerDataFields: JsonField> = JsonMissing.of(), + @JsonProperty("receiverUmaAddress") + @ExcludeMissing + receiverUmaAddress: JsonField = JsonMissing.of(), + ) : this( + lookupId, + supportedCurrencies, + requiredPayerDataFields, + receiverUmaAddress, + mutableMapOf(), + ) + + fun toLookupResponse(): LookupResponse = + LookupResponse.builder() + .lookupId(lookupId) + .supportedCurrencies(supportedCurrencies) + .requiredPayerDataFields(requiredPayerDataFields) + .build() + + /** + * Unique identifier for the lookup. Needed in the subsequent create quote request. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun lookupId(): String = lookupId.getRequired("lookupId") + + /** + * List of currencies supported by the receiving account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun supportedCurrencies(): List = + supportedCurrencies.getRequired("supportedCurrencies") + + /** + * Fields required by the receiving institution about the payer before payment can be completed + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun requiredPayerDataFields(): List? = + requiredPayerDataFields.getNullable("requiredPayerDataFields") + + /** + * The UMA address that was looked up + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun receiverUmaAddress(): String = receiverUmaAddress.getRequired("receiverUmaAddress") + + /** + * Returns the raw JSON value of [lookupId]. + * + * Unlike [lookupId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lookupId") @ExcludeMissing fun _lookupId(): JsonField = lookupId + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("supportedCurrencies") + @ExcludeMissing + fun _supportedCurrencies(): JsonField> = + supportedCurrencies + + /** + * Returns the raw JSON value of [requiredPayerDataFields]. + * + * Unlike [requiredPayerDataFields], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("requiredPayerDataFields") + @ExcludeMissing + fun _requiredPayerDataFields(): JsonField> = + requiredPayerDataFields + + /** + * Returns the raw JSON value of [receiverUmaAddress]. + * + * Unlike [receiverUmaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("receiverUmaAddress") + @ExcludeMissing + fun _receiverUmaAddress(): JsonField = receiverUmaAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ReceiverLookupUmaResponse]. + * + * The following fields are required: + * ```kotlin + * .lookupId() + * .supportedCurrencies() + * .receiverUmaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ReceiverLookupUmaResponse]. */ + class Builder internal constructor() { + + private var lookupId: JsonField? = null + private var supportedCurrencies: JsonField>? = + null + private var requiredPayerDataFields: JsonField>? = + null + private var receiverUmaAddress: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(receiverLookupUmaResponse: ReceiverLookupUmaResponse) = apply { + lookupId = receiverLookupUmaResponse.lookupId + supportedCurrencies = + receiverLookupUmaResponse.supportedCurrencies.map { it.toMutableList() } + requiredPayerDataFields = + receiverLookupUmaResponse.requiredPayerDataFields.map { it.toMutableList() } + receiverUmaAddress = receiverLookupUmaResponse.receiverUmaAddress + additionalProperties = receiverLookupUmaResponse.additionalProperties.toMutableMap() + } + + /** Unique identifier for the lookup. Needed in the subsequent create quote request. */ + fun lookupId(lookupId: String) = lookupId(JsonField.of(lookupId)) + + /** + * Sets [Builder.lookupId] to an arbitrary JSON value. + * + * You should usually call [Builder.lookupId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun lookupId(lookupId: JsonField) = apply { this.lookupId = lookupId } + + /** List of currencies supported by the receiving account */ + fun supportedCurrencies(supportedCurrencies: List) = + supportedCurrencies(JsonField.of(supportedCurrencies)) + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed + * `List` value instead. This method is primarily for + * setting the field to an undocumented or not yet supported value. + */ + fun supportedCurrencies( + supportedCurrencies: JsonField> + ) = apply { this.supportedCurrencies = supportedCurrencies.map { it.toMutableList() } } + + /** + * Adds a single [LookupResponse.SupportedCurrency] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: LookupResponse.SupportedCurrency) = apply { + supportedCurrencies = + (supportedCurrencies ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedCurrencies", it).add(supportedCurrency) + } + } + + /** + * Fields required by the receiving institution about the payer before payment can be + * completed + */ + fun requiredPayerDataFields(requiredPayerDataFields: List) = + requiredPayerDataFields(JsonField.of(requiredPayerDataFields)) + + /** + * Sets [Builder.requiredPayerDataFields] to an arbitrary JSON value. + * + * You should usually call [Builder.requiredPayerDataFields] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun requiredPayerDataFields( + requiredPayerDataFields: JsonField> + ) = apply { + this.requiredPayerDataFields = requiredPayerDataFields.map { it.toMutableList() } + } + + /** + * Adds a single [CounterpartyFieldDefinition] to [requiredPayerDataFields]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRequiredPayerDataField(requiredPayerDataField: CounterpartyFieldDefinition) = apply { + requiredPayerDataFields = + (requiredPayerDataFields ?: JsonField.of(mutableListOf())).also { + checkKnown("requiredPayerDataFields", it).add(requiredPayerDataField) + } + } + + /** The UMA address that was looked up */ + fun receiverUmaAddress(receiverUmaAddress: String) = + receiverUmaAddress(JsonField.of(receiverUmaAddress)) + + /** + * Sets [Builder.receiverUmaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.receiverUmaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receiverUmaAddress(receiverUmaAddress: JsonField) = apply { + this.receiverUmaAddress = receiverUmaAddress + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ReceiverLookupUmaResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .lookupId() + * .supportedCurrencies() + * .receiverUmaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ReceiverLookupUmaResponse = + ReceiverLookupUmaResponse( + checkRequired("lookupId", lookupId), + checkRequired("supportedCurrencies", supportedCurrencies).map { it.toImmutable() }, + (requiredPayerDataFields ?: JsonMissing.of()).map { it.toImmutable() }, + checkRequired("receiverUmaAddress", receiverUmaAddress), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ReceiverLookupUmaResponse = apply { + if (validated) { + return@apply + } + + lookupId() + supportedCurrencies().forEach { it.validate() } + requiredPayerDataFields()?.forEach { it.validate() } + receiverUmaAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (lookupId.asKnown() == null) 0 else 1) + + (supportedCurrencies.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (requiredPayerDataFields.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (receiverUmaAddress.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ReceiverLookupUmaResponse && + lookupId == other.lookupId && + supportedCurrencies == other.supportedCurrencies && + requiredPayerDataFields == other.requiredPayerDataFields && + receiverUmaAddress == other.receiverUmaAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + lookupId, + supportedCurrencies, + requiredPayerDataFields, + receiverUmaAddress, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ReceiverLookupUmaResponse{lookupId=$lookupId, supportedCurrencies=$supportedCurrencies, requiredPayerDataFields=$requiredPayerDataFields, receiverUmaAddress=$receiverUmaAddress, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParams.kt new file mode 100644 index 00000000..290f76b3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParams.kt @@ -0,0 +1,578 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Simulate sending funds to the bank account as instructed in the quote. This endpoint is only for + * the sandbox environment and will fail for production platforms/keys. + */ +class SandboxSendFundsParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Currency code for the funds to be sent + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun currencyCode(): String = body.currencyCode() + + /** + * The unique identifier of the quote + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun quoteId(): String = body.quoteId() + + /** + * The amount to send in the smallest unit of the currency (eg. cents). If not provided, the + * amount will be derived from the quote. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun currencyAmount(): Long? = body.currencyAmount() + + /** + * Returns the raw JSON value of [currencyCode]. + * + * Unlike [currencyCode], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _currencyCode(): JsonField = body._currencyCode() + + /** + * Returns the raw JSON value of [quoteId]. + * + * Unlike [quoteId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _quoteId(): JsonField = body._quoteId() + + /** + * Returns the raw JSON value of [currencyAmount]. + * + * Unlike [currencyAmount], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _currencyAmount(): JsonField = body._currencyAmount() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SandboxSendFundsParams]. + * + * The following fields are required: + * ```kotlin + * .currencyCode() + * .quoteId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SandboxSendFundsParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(sandboxSendFundsParams: SandboxSendFundsParams) = apply { + body = sandboxSendFundsParams.body.toBuilder() + additionalHeaders = sandboxSendFundsParams.additionalHeaders.toBuilder() + additionalQueryParams = sandboxSendFundsParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [currencyCode] + * - [quoteId] + * - [currencyAmount] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Currency code for the funds to be sent */ + fun currencyCode(currencyCode: String) = apply { body.currencyCode(currencyCode) } + + /** + * Sets [Builder.currencyCode] to an arbitrary JSON value. + * + * You should usually call [Builder.currencyCode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun currencyCode(currencyCode: JsonField) = apply { + body.currencyCode(currencyCode) + } + + /** The unique identifier of the quote */ + fun quoteId(quoteId: String) = apply { body.quoteId(quoteId) } + + /** + * Sets [Builder.quoteId] to an arbitrary JSON value. + * + * You should usually call [Builder.quoteId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun quoteId(quoteId: JsonField) = apply { body.quoteId(quoteId) } + + /** + * The amount to send in the smallest unit of the currency (eg. cents). If not provided, the + * amount will be derived from the quote. + */ + fun currencyAmount(currencyAmount: Long) = apply { body.currencyAmount(currencyAmount) } + + /** + * Sets [Builder.currencyAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.currencyAmount] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun currencyAmount(currencyAmount: JsonField) = apply { + body.currencyAmount(currencyAmount) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [SandboxSendFundsParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .currencyCode() + * .quoteId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SandboxSendFundsParams = + SandboxSendFundsParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currencyCode: JsonField, + private val quoteId: JsonField, + private val currencyAmount: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currencyCode") + @ExcludeMissing + currencyCode: JsonField = JsonMissing.of(), + @JsonProperty("quoteId") @ExcludeMissing quoteId: JsonField = JsonMissing.of(), + @JsonProperty("currencyAmount") + @ExcludeMissing + currencyAmount: JsonField = JsonMissing.of(), + ) : this(currencyCode, quoteId, currencyAmount, mutableMapOf()) + + /** + * Currency code for the funds to be sent + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun currencyCode(): String = currencyCode.getRequired("currencyCode") + + /** + * The unique identifier of the quote + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun quoteId(): String = quoteId.getRequired("quoteId") + + /** + * The amount to send in the smallest unit of the currency (eg. cents). If not provided, the + * amount will be derived from the quote. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun currencyAmount(): Long? = currencyAmount.getNullable("currencyAmount") + + /** + * Returns the raw JSON value of [currencyCode]. + * + * Unlike [currencyCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("currencyCode") + @ExcludeMissing + fun _currencyCode(): JsonField = currencyCode + + /** + * Returns the raw JSON value of [quoteId]. + * + * Unlike [quoteId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("quoteId") @ExcludeMissing fun _quoteId(): JsonField = quoteId + + /** + * Returns the raw JSON value of [currencyAmount]. + * + * Unlike [currencyAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("currencyAmount") + @ExcludeMissing + fun _currencyAmount(): JsonField = currencyAmount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .currencyCode() + * .quoteId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var currencyCode: JsonField? = null + private var quoteId: JsonField? = null + private var currencyAmount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + currencyCode = body.currencyCode + quoteId = body.quoteId + currencyAmount = body.currencyAmount + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Currency code for the funds to be sent */ + fun currencyCode(currencyCode: String) = currencyCode(JsonField.of(currencyCode)) + + /** + * Sets [Builder.currencyCode] to an arbitrary JSON value. + * + * You should usually call [Builder.currencyCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currencyCode(currencyCode: JsonField) = apply { + this.currencyCode = currencyCode + } + + /** The unique identifier of the quote */ + fun quoteId(quoteId: String) = quoteId(JsonField.of(quoteId)) + + /** + * Sets [Builder.quoteId] to an arbitrary JSON value. + * + * You should usually call [Builder.quoteId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun quoteId(quoteId: JsonField) = apply { this.quoteId = quoteId } + + /** + * The amount to send in the smallest unit of the currency (eg. cents). If not provided, + * the amount will be derived from the quote. + */ + fun currencyAmount(currencyAmount: Long) = currencyAmount(JsonField.of(currencyAmount)) + + /** + * Sets [Builder.currencyAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.currencyAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currencyAmount(currencyAmount: JsonField) = apply { + this.currencyAmount = currencyAmount + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .currencyCode() + * .quoteId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("currencyCode", currencyCode), + checkRequired("quoteId", quoteId), + currencyAmount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + currencyCode() + quoteId() + currencyAmount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currencyCode.asKnown() == null) 0 else 1) + + (if (quoteId.asKnown() == null) 0 else 1) + + (if (currencyAmount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + currencyCode == other.currencyCode && + quoteId == other.quoteId && + currencyAmount == other.currencyAmount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currencyCode, quoteId, currencyAmount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{currencyCode=$currencyCode, quoteId=$quoteId, currencyAmount=$currencyAmount, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SandboxSendFundsParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "SandboxSendFundsParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponse.kt new file mode 100644 index 00000000..c6aa3802 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponse.kt @@ -0,0 +1,1601 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.OutgoingRateDetails +import com.grid.api.models.quotes.PaymentInstructions +import com.grid.api.models.transactions.TransactionSourceOneOf +import com.grid.api.models.transactions.TransactionStatus +import com.grid.api.models.transactions.TransactionType +import com.grid.api.models.transferin.Transaction +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class SandboxSendFundsResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val customerId: JsonField, + private val destination: JsonField, + private val platformCustomerId: JsonField, + private val status: JsonField, + private val type: JsonField, + private val counterpartyInformation: JsonField, + private val createdAt: JsonField, + private val description: JsonField, + private val settledAt: JsonField, + private val updatedAt: JsonField, + private val paymentInstructions: JsonField>, + private val sentAmount: JsonField, + private val source: JsonField, + private val exchangeRate: JsonField, + private val failureReason: JsonField, + private val fees: JsonField, + private val originalTransactionId: JsonField, + private val quoteId: JsonField, + private val rateDetails: JsonField, + private val receivedAmount: JsonField, + private val refund: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("status") + @ExcludeMissing + status: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("counterpartyInformation") + @ExcludeMissing + counterpartyInformation: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("description") + @ExcludeMissing + description: JsonField = JsonMissing.of(), + @JsonProperty("settledAt") + @ExcludeMissing + settledAt: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("paymentInstructions") + @ExcludeMissing + paymentInstructions: JsonField> = JsonMissing.of(), + @JsonProperty("sentAmount") + @ExcludeMissing + sentAmount: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + @JsonProperty("exchangeRate") + @ExcludeMissing + exchangeRate: JsonField = JsonMissing.of(), + @JsonProperty("failureReason") + @ExcludeMissing + failureReason: JsonField = JsonMissing.of(), + @JsonProperty("fees") @ExcludeMissing fees: JsonField = JsonMissing.of(), + @JsonProperty("originalTransactionId") + @ExcludeMissing + originalTransactionId: JsonField = JsonMissing.of(), + @JsonProperty("quoteId") @ExcludeMissing quoteId: JsonField = JsonMissing.of(), + @JsonProperty("rateDetails") + @ExcludeMissing + rateDetails: JsonField = JsonMissing.of(), + @JsonProperty("receivedAmount") + @ExcludeMissing + receivedAmount: JsonField = JsonMissing.of(), + @JsonProperty("refund") @ExcludeMissing refund: JsonField = JsonMissing.of(), + ) : this( + id, + customerId, + destination, + platformCustomerId, + status, + type, + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + paymentInstructions, + sentAmount, + source, + exchangeRate, + failureReason, + fees, + originalTransactionId, + quoteId, + rateDetails, + receivedAmount, + refund, + mutableMapOf(), + ) + + fun toTransaction(): Transaction = + Transaction.builder() + .id(id) + .customerId(customerId) + .destination(destination) + .platformCustomerId(platformCustomerId) + .status(status) + .type(type) + .counterpartyInformation(counterpartyInformation) + .createdAt(createdAt) + .description(description) + .settledAt(settledAt) + .updatedAt(updatedAt) + .build() + + /** + * Unique identifier for the transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * System ID of the customer (sender for outgoing, recipient for incoming) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerId(): String = customerId.getRequired("customerId") + + /** + * Destination account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Transaction.Destination = destination.getRequired("destination") + + /** + * Platform-specific ID of the customer (sender for outgoing, recipient for incoming) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Status of a payment transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): TransactionStatus = status.getRequired("status") + + /** + * Type of transaction (incoming payment or outgoing payment) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): TransactionType = type.getRequired("type") + + /** + * Additional information about the counterparty, if available and relevant to the transaction + * and platform. Only applicable for transactions to/from UMA addresses. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun counterpartyInformation(): Transaction.CounterpartyInformation? = + counterpartyInformation.getNullable("counterpartyInformation") + + /** + * When the transaction was created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Optional memo or description for the payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun description(): String? = description.getNullable("description") + + /** + * When the payment was or will be settled + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun settledAt(): OffsetDateTime? = settledAt.getNullable("settledAt") + + /** + * When the transaction was last updated + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * Payment instructions for executing the payment. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun paymentInstructions(): List = + paymentInstructions.getRequired("paymentInstructions") + + /** + * Amount sent in the sender's currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun sentAmount(): CurrencyAmount = sentAmount.getRequired("sentAmount") + + /** + * Source account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): TransactionSourceOneOf = source.getRequired("source") + + /** + * Number of sending currency units per receiving currency unit. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun exchangeRate(): Double? = exchangeRate.getNullable("exchangeRate") + + /** + * If the transaction failed, this field provides the reason for failure. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun failureReason(): FailureReason? = failureReason.getNullable("failureReason") + + /** + * The fees associated with the quote in the smallest unit of the sending currency (eg. cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun fees(): Long? = fees.getNullable("fees") + + /** + * ID of the original transaction that this transaction is retrying, if applicable + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun originalTransactionId(): String? = + originalTransactionId.getNullable("originalTransactionId") + + /** + * The ID of the quote that was used to trigger this payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun quoteId(): String? = quoteId.getNullable("quoteId") + + /** + * Details about the rate and fees for the transaction. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun rateDetails(): OutgoingRateDetails? = rateDetails.getNullable("rateDetails") + + /** + * Amount to be received by recipient in the recipient's currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun receivedAmount(): CurrencyAmount? = receivedAmount.getNullable("receivedAmount") + + /** + * The refund if transaction was refunded. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun refund(): Refund? = refund.getNullable("refund") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + /** + * Returns the raw JSON value of [counterpartyInformation]. + * + * Unlike [counterpartyInformation], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("counterpartyInformation") + @ExcludeMissing + fun _counterpartyInformation(): JsonField = + counterpartyInformation + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [settledAt]. + * + * Unlike [settledAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("settledAt") + @ExcludeMissing + fun _settledAt(): JsonField = settledAt + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [paymentInstructions]. + * + * Unlike [paymentInstructions], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("paymentInstructions") + @ExcludeMissing + fun _paymentInstructions(): JsonField> = paymentInstructions + + /** + * Returns the raw JSON value of [sentAmount]. + * + * Unlike [sentAmount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sentAmount") + @ExcludeMissing + fun _sentAmount(): JsonField = sentAmount + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") + @ExcludeMissing + fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [exchangeRate]. + * + * Unlike [exchangeRate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("exchangeRate") + @ExcludeMissing + fun _exchangeRate(): JsonField = exchangeRate + + /** + * Returns the raw JSON value of [failureReason]. + * + * Unlike [failureReason], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("failureReason") + @ExcludeMissing + fun _failureReason(): JsonField = failureReason + + /** + * Returns the raw JSON value of [fees]. + * + * Unlike [fees], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("fees") @ExcludeMissing fun _fees(): JsonField = fees + + /** + * Returns the raw JSON value of [originalTransactionId]. + * + * Unlike [originalTransactionId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("originalTransactionId") + @ExcludeMissing + fun _originalTransactionId(): JsonField = originalTransactionId + + /** + * Returns the raw JSON value of [quoteId]. + * + * Unlike [quoteId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("quoteId") @ExcludeMissing fun _quoteId(): JsonField = quoteId + + /** + * Returns the raw JSON value of [rateDetails]. + * + * Unlike [rateDetails], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("rateDetails") + @ExcludeMissing + fun _rateDetails(): JsonField = rateDetails + + /** + * Returns the raw JSON value of [receivedAmount]. + * + * Unlike [receivedAmount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("receivedAmount") + @ExcludeMissing + fun _receivedAmount(): JsonField = receivedAmount + + /** + * Returns the raw JSON value of [refund]. + * + * Unlike [refund], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("refund") @ExcludeMissing fun _refund(): JsonField = refund + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [SandboxSendFundsResponse]. + * + * The following fields are required: + * ```kotlin + * .id() + * .customerId() + * .destination() + * .platformCustomerId() + * .status() + * .type() + * .paymentInstructions() + * .sentAmount() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [SandboxSendFundsResponse]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var customerId: JsonField? = null + private var destination: JsonField? = null + private var platformCustomerId: JsonField? = null + private var status: JsonField? = null + private var type: JsonField? = null + private var counterpartyInformation: JsonField = + JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var description: JsonField = JsonMissing.of() + private var settledAt: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var paymentInstructions: JsonField>? = null + private var sentAmount: JsonField? = null + private var source: JsonField? = null + private var exchangeRate: JsonField = JsonMissing.of() + private var failureReason: JsonField = JsonMissing.of() + private var fees: JsonField = JsonMissing.of() + private var originalTransactionId: JsonField = JsonMissing.of() + private var quoteId: JsonField = JsonMissing.of() + private var rateDetails: JsonField = JsonMissing.of() + private var receivedAmount: JsonField = JsonMissing.of() + private var refund: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(sandboxSendFundsResponse: SandboxSendFundsResponse) = apply { + id = sandboxSendFundsResponse.id + customerId = sandboxSendFundsResponse.customerId + destination = sandboxSendFundsResponse.destination + platformCustomerId = sandboxSendFundsResponse.platformCustomerId + status = sandboxSendFundsResponse.status + type = sandboxSendFundsResponse.type + counterpartyInformation = sandboxSendFundsResponse.counterpartyInformation + createdAt = sandboxSendFundsResponse.createdAt + description = sandboxSendFundsResponse.description + settledAt = sandboxSendFundsResponse.settledAt + updatedAt = sandboxSendFundsResponse.updatedAt + paymentInstructions = + sandboxSendFundsResponse.paymentInstructions.map { it.toMutableList() } + sentAmount = sandboxSendFundsResponse.sentAmount + source = sandboxSendFundsResponse.source + exchangeRate = sandboxSendFundsResponse.exchangeRate + failureReason = sandboxSendFundsResponse.failureReason + fees = sandboxSendFundsResponse.fees + originalTransactionId = sandboxSendFundsResponse.originalTransactionId + quoteId = sandboxSendFundsResponse.quoteId + rateDetails = sandboxSendFundsResponse.rateDetails + receivedAmount = sandboxSendFundsResponse.receivedAmount + refund = sandboxSendFundsResponse.refund + additionalProperties = sandboxSendFundsResponse.additionalProperties.toMutableMap() + } + + /** Unique identifier for the transaction */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** System ID of the customer (sender for outgoing, recipient for incoming) */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** Destination account details */ + fun destination(destination: Transaction.Destination) = + destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Transaction.Destination] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Alias for calling [destination] with `Transaction.Destination.ofAccount(account)`. */ + fun destination(account: Transaction.Destination.Account) = + destination(Transaction.Destination.ofAccount(account)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * Transaction.Destination.Account.builder() + * .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountDestination(accountId: String) = + destination( + Transaction.Destination.Account.builder() + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** + * Alias for calling [destination] with `Transaction.Destination.ofUmaAddress(umaAddress)`. + */ + fun destination(umaAddress: Transaction.Destination.UmaAddress) = + destination(Transaction.Destination.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * Transaction.Destination.UmaAddress.builder() + * .destinationType(Transaction.Destination.UmaAddress.DestinationType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressDestination(umaAddress: String) = + destination( + Transaction.Destination.UmaAddress.builder() + .destinationType(Transaction.Destination.UmaAddress.DestinationType.UMA_ADDRESS) + .umaAddress(umaAddress) + .build() + ) + + /** Platform-specific ID of the customer (sender for outgoing, recipient for incoming) */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** Status of a payment transaction */ + fun status(status: TransactionStatus) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [TransactionStatus] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** Type of transaction (incoming payment or outgoing payment) */ + fun type(type: TransactionType) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [TransactionType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + /** + * Additional information about the counterparty, if available and relevant to the + * transaction and platform. Only applicable for transactions to/from UMA addresses. + */ + fun counterpartyInformation(counterpartyInformation: Transaction.CounterpartyInformation) = + counterpartyInformation(JsonField.of(counterpartyInformation)) + + /** + * Sets [Builder.counterpartyInformation] to an arbitrary JSON value. + * + * You should usually call [Builder.counterpartyInformation] with a well-typed + * [Transaction.CounterpartyInformation] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun counterpartyInformation( + counterpartyInformation: JsonField + ) = apply { this.counterpartyInformation = counterpartyInformation } + + /** When the transaction was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Optional memo or description for the payment */ + fun description(description: String) = description(JsonField.of(description)) + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun description(description: JsonField) = apply { this.description = description } + + /** When the payment was or will be settled */ + fun settledAt(settledAt: OffsetDateTime) = settledAt(JsonField.of(settledAt)) + + /** + * Sets [Builder.settledAt] to an arbitrary JSON value. + * + * You should usually call [Builder.settledAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun settledAt(settledAt: JsonField) = apply { this.settledAt = settledAt } + + /** When the transaction was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + /** Payment instructions for executing the payment. */ + fun paymentInstructions(paymentInstructions: List) = + paymentInstructions(JsonField.of(paymentInstructions)) + + /** + * Sets [Builder.paymentInstructions] to an arbitrary JSON value. + * + * You should usually call [Builder.paymentInstructions] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun paymentInstructions(paymentInstructions: JsonField>) = apply { + this.paymentInstructions = paymentInstructions.map { it.toMutableList() } + } + + /** + * Adds a single [PaymentInstructions] to [paymentInstructions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addPaymentInstruction(paymentInstruction: PaymentInstructions) = apply { + paymentInstructions = + (paymentInstructions ?: JsonField.of(mutableListOf())).also { + checkKnown("paymentInstructions", it).add(paymentInstruction) + } + } + + /** Amount sent in the sender's currency */ + fun sentAmount(sentAmount: CurrencyAmount) = sentAmount(JsonField.of(sentAmount)) + + /** + * Sets [Builder.sentAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.sentAmount] with a well-typed [CurrencyAmount] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sentAmount(sentAmount: JsonField) = apply { + this.sentAmount = sentAmount + } + + /** Source account details */ + fun source(source: TransactionSourceOneOf) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [TransactionSourceOneOf] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** Alias for calling [source] with `TransactionSourceOneOf.ofAccount(account)`. */ + fun source(account: TransactionSourceOneOf.Account) = + source(TransactionSourceOneOf.ofAccount(account)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * TransactionSourceOneOf.Account.builder() + * .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountSource(accountId: String) = + source( + TransactionSourceOneOf.Account.builder() + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** Alias for calling [source] with `TransactionSourceOneOf.ofUmaAddress(umaAddress)`. */ + fun source(umaAddress: TransactionSourceOneOf.UmaAddress) = + source(TransactionSourceOneOf.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * TransactionSourceOneOf.UmaAddress.builder() + * .sourceType(TransactionSourceOneOf.UmaAddress.SourceType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressSource(umaAddress: String) = + source( + TransactionSourceOneOf.UmaAddress.builder() + .sourceType(TransactionSourceOneOf.UmaAddress.SourceType.UMA_ADDRESS) + .umaAddress(umaAddress) + .build() + ) + + /** Number of sending currency units per receiving currency unit. */ + fun exchangeRate(exchangeRate: Double) = exchangeRate(JsonField.of(exchangeRate)) + + /** + * Sets [Builder.exchangeRate] to an arbitrary JSON value. + * + * You should usually call [Builder.exchangeRate] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun exchangeRate(exchangeRate: JsonField) = apply { + this.exchangeRate = exchangeRate + } + + /** If the transaction failed, this field provides the reason for failure. */ + fun failureReason(failureReason: FailureReason) = failureReason(JsonField.of(failureReason)) + + /** + * Sets [Builder.failureReason] to an arbitrary JSON value. + * + * You should usually call [Builder.failureReason] with a well-typed [FailureReason] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun failureReason(failureReason: JsonField) = apply { + this.failureReason = failureReason + } + + /** + * The fees associated with the quote in the smallest unit of the sending currency (eg. + * cents). + */ + fun fees(fees: Long) = fees(JsonField.of(fees)) + + /** + * Sets [Builder.fees] to an arbitrary JSON value. + * + * You should usually call [Builder.fees] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun fees(fees: JsonField) = apply { this.fees = fees } + + /** ID of the original transaction that this transaction is retrying, if applicable */ + fun originalTransactionId(originalTransactionId: String) = + originalTransactionId(JsonField.of(originalTransactionId)) + + /** + * Sets [Builder.originalTransactionId] to an arbitrary JSON value. + * + * You should usually call [Builder.originalTransactionId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun originalTransactionId(originalTransactionId: JsonField) = apply { + this.originalTransactionId = originalTransactionId + } + + /** The ID of the quote that was used to trigger this payment */ + fun quoteId(quoteId: String) = quoteId(JsonField.of(quoteId)) + + /** + * Sets [Builder.quoteId] to an arbitrary JSON value. + * + * You should usually call [Builder.quoteId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun quoteId(quoteId: JsonField) = apply { this.quoteId = quoteId } + + /** Details about the rate and fees for the transaction. */ + fun rateDetails(rateDetails: OutgoingRateDetails) = rateDetails(JsonField.of(rateDetails)) + + /** + * Sets [Builder.rateDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.rateDetails] with a well-typed [OutgoingRateDetails] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun rateDetails(rateDetails: JsonField) = apply { + this.rateDetails = rateDetails + } + + /** Amount to be received by recipient in the recipient's currency */ + fun receivedAmount(receivedAmount: CurrencyAmount) = + receivedAmount(JsonField.of(receivedAmount)) + + /** + * Sets [Builder.receivedAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.receivedAmount] with a well-typed [CurrencyAmount] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receivedAmount(receivedAmount: JsonField) = apply { + this.receivedAmount = receivedAmount + } + + /** The refund if transaction was refunded. */ + fun refund(refund: Refund) = refund(JsonField.of(refund)) + + /** + * Sets [Builder.refund] to an arbitrary JSON value. + * + * You should usually call [Builder.refund] with a well-typed [Refund] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun refund(refund: JsonField) = apply { this.refund = refund } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [SandboxSendFundsResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .id() + * .customerId() + * .destination() + * .platformCustomerId() + * .status() + * .type() + * .paymentInstructions() + * .sentAmount() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): SandboxSendFundsResponse = + SandboxSendFundsResponse( + checkRequired("id", id), + checkRequired("customerId", customerId), + checkRequired("destination", destination), + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("status", status), + checkRequired("type", type), + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + checkRequired("paymentInstructions", paymentInstructions).map { it.toImmutable() }, + checkRequired("sentAmount", sentAmount), + checkRequired("source", source), + exchangeRate, + failureReason, + fees, + originalTransactionId, + quoteId, + rateDetails, + receivedAmount, + refund, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): SandboxSendFundsResponse = apply { + if (validated) { + return@apply + } + + id() + customerId() + destination().validate() + platformCustomerId() + status().validate() + type().validate() + counterpartyInformation()?.validate() + createdAt() + description() + settledAt() + updatedAt() + paymentInstructions().forEach { it.validate() } + sentAmount().validate() + source().validate() + exchangeRate() + failureReason()?.validate() + fees() + originalTransactionId() + quoteId() + rateDetails()?.validate() + receivedAmount()?.validate() + refund()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (if (customerId.asKnown() == null) 0 else 1) + + (destination.asKnown()?.validity() ?: 0) + + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (status.asKnown()?.validity() ?: 0) + + (type.asKnown()?.validity() ?: 0) + + (counterpartyInformation.asKnown()?.validity() ?: 0) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (description.asKnown() == null) 0 else 1) + + (if (settledAt.asKnown() == null) 0 else 1) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (paymentInstructions.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (sentAmount.asKnown()?.validity() ?: 0) + + (source.asKnown()?.validity() ?: 0) + + (if (exchangeRate.asKnown() == null) 0 else 1) + + (failureReason.asKnown()?.validity() ?: 0) + + (if (fees.asKnown() == null) 0 else 1) + + (if (originalTransactionId.asKnown() == null) 0 else 1) + + (if (quoteId.asKnown() == null) 0 else 1) + + (rateDetails.asKnown()?.validity() ?: 0) + + (receivedAmount.asKnown()?.validity() ?: 0) + + (refund.asKnown()?.validity() ?: 0) + + /** If the transaction failed, this field provides the reason for failure. */ + class FailureReason @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val QUOTE_EXPIRED = of("QUOTE_EXPIRED") + + val QUOTE_EXECUTION_FAILED = of("QUOTE_EXECUTION_FAILED") + + val LIGHTNING_PAYMENT_FAILED = of("LIGHTNING_PAYMENT_FAILED") + + val FUNDING_AMOUNT_MISMATCH = of("FUNDING_AMOUNT_MISMATCH") + + val COUNTERPARTY_POST_TX_FAILED = of("COUNTERPARTY_POST_TX_FAILED") + + val TIMEOUT = of("TIMEOUT") + + fun of(value: String) = FailureReason(JsonField.of(value)) + } + + /** An enum containing [FailureReason]'s known values. */ + enum class Known { + QUOTE_EXPIRED, + QUOTE_EXECUTION_FAILED, + LIGHTNING_PAYMENT_FAILED, + FUNDING_AMOUNT_MISMATCH, + COUNTERPARTY_POST_TX_FAILED, + TIMEOUT, + } + + /** + * An enum containing [FailureReason]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [FailureReason] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + QUOTE_EXPIRED, + QUOTE_EXECUTION_FAILED, + LIGHTNING_PAYMENT_FAILED, + FUNDING_AMOUNT_MISMATCH, + COUNTERPARTY_POST_TX_FAILED, + TIMEOUT, + /** + * An enum member indicating that [FailureReason] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + QUOTE_EXPIRED -> Value.QUOTE_EXPIRED + QUOTE_EXECUTION_FAILED -> Value.QUOTE_EXECUTION_FAILED + LIGHTNING_PAYMENT_FAILED -> Value.LIGHTNING_PAYMENT_FAILED + FUNDING_AMOUNT_MISMATCH -> Value.FUNDING_AMOUNT_MISMATCH + COUNTERPARTY_POST_TX_FAILED -> Value.COUNTERPARTY_POST_TX_FAILED + TIMEOUT -> Value.TIMEOUT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + QUOTE_EXPIRED -> Known.QUOTE_EXPIRED + QUOTE_EXECUTION_FAILED -> Known.QUOTE_EXECUTION_FAILED + LIGHTNING_PAYMENT_FAILED -> Known.LIGHTNING_PAYMENT_FAILED + FUNDING_AMOUNT_MISMATCH -> Known.FUNDING_AMOUNT_MISMATCH + COUNTERPARTY_POST_TX_FAILED -> Known.COUNTERPARTY_POST_TX_FAILED + TIMEOUT -> Known.TIMEOUT + else -> throw GridInvalidDataException("Unknown FailureReason: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): FailureReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FailureReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The refund if transaction was refunded. */ + class Refund + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val initiatedAt: JsonField, + private val reference: JsonField, + private val settledAt: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("initiatedAt") + @ExcludeMissing + initiatedAt: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + @JsonProperty("settledAt") + @ExcludeMissing + settledAt: JsonField = JsonMissing.of(), + ) : this(initiatedAt, reference, settledAt, mutableMapOf()) + + /** + * When the refund was initiated + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun initiatedAt(): OffsetDateTime = initiatedAt.getRequired("initiatedAt") + + /** + * The unique reference code of the refund + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * When the refund was or will be settled + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun settledAt(): OffsetDateTime? = settledAt.getNullable("settledAt") + + /** + * Returns the raw JSON value of [initiatedAt]. + * + * Unlike [initiatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("initiatedAt") + @ExcludeMissing + fun _initiatedAt(): JsonField = initiatedAt + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference + + /** + * Returns the raw JSON value of [settledAt]. + * + * Unlike [settledAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("settledAt") + @ExcludeMissing + fun _settledAt(): JsonField = settledAt + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Refund]. + * + * The following fields are required: + * ```kotlin + * .initiatedAt() + * .reference() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Refund]. */ + class Builder internal constructor() { + + private var initiatedAt: JsonField? = null + private var reference: JsonField? = null + private var settledAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(refund: Refund) = apply { + initiatedAt = refund.initiatedAt + reference = refund.reference + settledAt = refund.settledAt + additionalProperties = refund.additionalProperties.toMutableMap() + } + + /** When the refund was initiated */ + fun initiatedAt(initiatedAt: OffsetDateTime) = initiatedAt(JsonField.of(initiatedAt)) + + /** + * Sets [Builder.initiatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.initiatedAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun initiatedAt(initiatedAt: JsonField) = apply { + this.initiatedAt = initiatedAt + } + + /** The unique reference code of the refund */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + /** When the refund was or will be settled */ + fun settledAt(settledAt: OffsetDateTime) = settledAt(JsonField.of(settledAt)) + + /** + * Sets [Builder.settledAt] to an arbitrary JSON value. + * + * You should usually call [Builder.settledAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun settledAt(settledAt: JsonField) = apply { + this.settledAt = settledAt + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Refund]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .initiatedAt() + * .reference() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Refund = + Refund( + checkRequired("initiatedAt", initiatedAt), + checkRequired("reference", reference), + settledAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Refund = apply { + if (validated) { + return@apply + } + + initiatedAt() + reference() + settledAt() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (initiatedAt.asKnown() == null) 0 else 1) + + (if (reference.asKnown() == null) 0 else 1) + + (if (settledAt.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Refund && + initiatedAt == other.initiatedAt && + reference == other.reference && + settledAt == other.settledAt && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(initiatedAt, reference, settledAt, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Refund{initiatedAt=$initiatedAt, reference=$reference, settledAt=$settledAt, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SandboxSendFundsResponse && + id == other.id && + customerId == other.customerId && + destination == other.destination && + platformCustomerId == other.platformCustomerId && + status == other.status && + type == other.type && + counterpartyInformation == other.counterpartyInformation && + createdAt == other.createdAt && + description == other.description && + settledAt == other.settledAt && + updatedAt == other.updatedAt && + paymentInstructions == other.paymentInstructions && + sentAmount == other.sentAmount && + source == other.source && + exchangeRate == other.exchangeRate && + failureReason == other.failureReason && + fees == other.fees && + originalTransactionId == other.originalTransactionId && + quoteId == other.quoteId && + rateDetails == other.rateDetails && + receivedAmount == other.receivedAmount && + refund == other.refund && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + customerId, + destination, + platformCustomerId, + status, + type, + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + paymentInstructions, + sentAmount, + source, + exchangeRate, + failureReason, + fees, + originalTransactionId, + quoteId, + rateDetails, + receivedAmount, + refund, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "SandboxSendFundsResponse{id=$id, customerId=$customerId, destination=$destination, platformCustomerId=$platformCustomerId, status=$status, type=$type, counterpartyInformation=$counterpartyInformation, createdAt=$createdAt, description=$description, settledAt=$settledAt, updatedAt=$updatedAt, paymentInstructions=$paymentInstructions, sentAmount=$sentAmount, source=$source, exchangeRate=$exchangeRate, failureReason=$failureReason, fees=$fees, originalTransactionId=$originalTransactionId, quoteId=$quoteId, rateDetails=$rateDetails, receivedAmount=$receivedAmount, refund=$refund, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccount.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccount.kt new file mode 100644 index 00000000..220e0122 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccount.kt @@ -0,0 +1,417 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox.internalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.PaymentInstructions +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class InternalAccount +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val balance: JsonField, + private val createdAt: JsonField, + private val fundingPaymentInstructions: JsonField>, + private val updatedAt: JsonField, + private val customerId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("balance") + @ExcludeMissing + balance: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("fundingPaymentInstructions") + @ExcludeMissing + fundingPaymentInstructions: JsonField> = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("customerId") @ExcludeMissing customerId: JsonField = JsonMissing.of(), + ) : this( + id, + balance, + createdAt, + fundingPaymentInstructions, + updatedAt, + customerId, + mutableMapOf(), + ) + + /** + * The ID of the internal account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun balance(): CurrencyAmount = balance.getRequired("balance") + + /** + * Timestamp when the internal account was created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime = createdAt.getRequired("createdAt") + + /** + * Payment instructions for funding the account + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun fundingPaymentInstructions(): List = + fundingPaymentInstructions.getRequired("fundingPaymentInstructions") + + /** + * Timestamp when the internal account was last updated + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime = updatedAt.getRequired("updatedAt") + + /** + * The ID of the customer associated with the internal account. If this field is empty, the + * internal account belongs to the platform. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [balance]. + * + * Unlike [balance], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("balance") @ExcludeMissing fun _balance(): JsonField = balance + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [fundingPaymentInstructions]. + * + * Unlike [fundingPaymentInstructions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("fundingPaymentInstructions") + @ExcludeMissing + fun _fundingPaymentInstructions(): JsonField> = + fundingPaymentInstructions + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InternalAccount]. + * + * The following fields are required: + * ```kotlin + * .id() + * .balance() + * .createdAt() + * .fundingPaymentInstructions() + * .updatedAt() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [InternalAccount]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var balance: JsonField? = null + private var createdAt: JsonField? = null + private var fundingPaymentInstructions: JsonField>? = null + private var updatedAt: JsonField? = null + private var customerId: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(internalAccount: InternalAccount) = apply { + id = internalAccount.id + balance = internalAccount.balance + createdAt = internalAccount.createdAt + fundingPaymentInstructions = + internalAccount.fundingPaymentInstructions.map { it.toMutableList() } + updatedAt = internalAccount.updatedAt + customerId = internalAccount.customerId + additionalProperties = internalAccount.additionalProperties.toMutableMap() + } + + /** The ID of the internal account */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + fun balance(balance: CurrencyAmount) = balance(JsonField.of(balance)) + + /** + * Sets [Builder.balance] to an arbitrary JSON value. + * + * You should usually call [Builder.balance] with a well-typed [CurrencyAmount] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun balance(balance: JsonField) = apply { this.balance = balance } + + /** Timestamp when the internal account was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Payment instructions for funding the account */ + fun fundingPaymentInstructions(fundingPaymentInstructions: List) = + fundingPaymentInstructions(JsonField.of(fundingPaymentInstructions)) + + /** + * Sets [Builder.fundingPaymentInstructions] to an arbitrary JSON value. + * + * You should usually call [Builder.fundingPaymentInstructions] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun fundingPaymentInstructions( + fundingPaymentInstructions: JsonField> + ) = apply { + this.fundingPaymentInstructions = fundingPaymentInstructions.map { it.toMutableList() } + } + + /** + * Adds a single [PaymentInstructions] to [fundingPaymentInstructions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addFundingPaymentInstruction(fundingPaymentInstruction: PaymentInstructions) = apply { + fundingPaymentInstructions = + (fundingPaymentInstructions ?: JsonField.of(mutableListOf())).also { + checkKnown("fundingPaymentInstructions", it).add(fundingPaymentInstruction) + } + } + + /** Timestamp when the internal account was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + /** + * The ID of the customer associated with the internal account. If this field is empty, the + * internal account belongs to the platform. + */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [InternalAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .id() + * .balance() + * .createdAt() + * .fundingPaymentInstructions() + * .updatedAt() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InternalAccount = + InternalAccount( + checkRequired("id", id), + checkRequired("balance", balance), + checkRequired("createdAt", createdAt), + checkRequired("fundingPaymentInstructions", fundingPaymentInstructions).map { + it.toImmutable() + }, + checkRequired("updatedAt", updatedAt), + customerId, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InternalAccount = apply { + if (validated) { + return@apply + } + + id() + balance().validate() + createdAt() + fundingPaymentInstructions().forEach { it.validate() } + updatedAt() + customerId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (balance.asKnown()?.validity() ?: 0) + + (if (createdAt.asKnown() == null) 0 else 1) + + (fundingPaymentInstructions.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (if (customerId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InternalAccount && + id == other.id && + balance == other.balance && + createdAt == other.createdAt && + fundingPaymentInstructions == other.fundingPaymentInstructions && + updatedAt == other.updatedAt && + customerId == other.customerId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + balance, + createdAt, + fundingPaymentInstructions, + updatedAt, + customerId, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InternalAccount{id=$id, balance=$balance, createdAt=$createdAt, fundingPaymentInstructions=$fundingPaymentInstructions, updatedAt=$updatedAt, customerId=$customerId, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParams.kt new file mode 100644 index 00000000..8ced25ec --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParams.kt @@ -0,0 +1,441 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox.internalaccounts + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Simulate receiving funds into an internal account in the sandbox environment. This is useful for + * testing scenarios where you need to add funds to a customer's or platform's internal account + * without going through a real bank transfer or following payment instructions. This endpoint is + * only for the sandbox environment and will fail for production platforms/keys. + */ +class InternalAccountFundParams +private constructor( + private val accountId: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun accountId(): String? = accountId + + /** + * Amount to add in the smallest unit of the account's currency (e.g., cents for USD/EUR, + * satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun amount(): Long = body.amount() + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _amount(): JsonField = body._amount() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InternalAccountFundParams]. + * + * The following fields are required: + * ```kotlin + * .amount() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [InternalAccountFundParams]. */ + class Builder internal constructor() { + + private var accountId: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(internalAccountFundParams: InternalAccountFundParams) = apply { + accountId = internalAccountFundParams.accountId + body = internalAccountFundParams.body.toBuilder() + additionalHeaders = internalAccountFundParams.additionalHeaders.toBuilder() + additionalQueryParams = internalAccountFundParams.additionalQueryParams.toBuilder() + } + + fun accountId(accountId: String?) = apply { this.accountId = accountId } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [amount] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * Amount to add in the smallest unit of the account's currency (e.g., cents for USD/EUR, + * satoshis for BTC) + */ + fun amount(amount: Long) = apply { body.amount(amount) } + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun amount(amount: JsonField) = apply { body.amount(amount) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [InternalAccountFundParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .amount() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InternalAccountFundParams = + InternalAccountFundParams( + accountId, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> accountId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val amount: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("amount") @ExcludeMissing amount: JsonField = JsonMissing.of() + ) : this(amount, mutableMapOf()) + + /** + * Amount to add in the smallest unit of the account's currency (e.g., cents for USD/EUR, + * satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun amount(): Long = amount.getRequired("amount") + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amount") @ExcludeMissing fun _amount(): JsonField = amount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .amount() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var amount: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + amount = body.amount + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * Amount to add in the smallest unit of the account's currency (e.g., cents for + * USD/EUR, satoshis for BTC) + */ + fun amount(amount: Long) = amount(JsonField.of(amount)) + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun amount(amount: JsonField) = apply { this.amount = amount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .amount() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body(checkRequired("amount", amount), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + amount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (amount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + amount == other.amount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(amount, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{amount=$amount, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InternalAccountFundParams && + accountId == other.accountId && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(accountId, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InternalAccountFundParams{accountId=$accountId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParams.kt new file mode 100644 index 00000000..0d64b2bb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParams.kt @@ -0,0 +1,757 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox.uma + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Simulate sending payment from an sandbox uma address to a platform customer to test payment + * receive. This endpoint is only for the sandbox environment and will fail for production + * platforms/keys. + */ +class UmaReceivePaymentParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The amount to be received in the smallest unit of the currency (eg. cents) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivingCurrencyAmount(): Long = body.receivingCurrencyAmount() + + /** + * The currency code for the receiving amount + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivingCurrencyCode(): String = body.receivingCurrencyCode() + + /** + * UMA address of the sender from the sandbox + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun senderUmaAddress(): String = body.senderUmaAddress() + + /** + * System ID of the receiver (optional if receiverUmaAddress is provided) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun customerId(): String? = body.customerId() + + /** + * UMA address of the receiver (optional if customerId is provided) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun receiverUmaAddress(): String? = body.receiverUmaAddress() + + /** + * Returns the raw JSON value of [receivingCurrencyAmount]. + * + * Unlike [receivingCurrencyAmount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + fun _receivingCurrencyAmount(): JsonField = body._receivingCurrencyAmount() + + /** + * Returns the raw JSON value of [receivingCurrencyCode]. + * + * Unlike [receivingCurrencyCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _receivingCurrencyCode(): JsonField = body._receivingCurrencyCode() + + /** + * Returns the raw JSON value of [senderUmaAddress]. + * + * Unlike [senderUmaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _senderUmaAddress(): JsonField = body._senderUmaAddress() + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _customerId(): JsonField = body._customerId() + + /** + * Returns the raw JSON value of [receiverUmaAddress]. + * + * Unlike [receiverUmaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _receiverUmaAddress(): JsonField = body._receiverUmaAddress() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaReceivePaymentParams]. + * + * The following fields are required: + * ```kotlin + * .receivingCurrencyAmount() + * .receivingCurrencyCode() + * .senderUmaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaReceivePaymentParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(umaReceivePaymentParams: UmaReceivePaymentParams) = apply { + body = umaReceivePaymentParams.body.toBuilder() + additionalHeaders = umaReceivePaymentParams.additionalHeaders.toBuilder() + additionalQueryParams = umaReceivePaymentParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [receivingCurrencyAmount] + * - [receivingCurrencyCode] + * - [senderUmaAddress] + * - [customerId] + * - [receiverUmaAddress] + * - etc. + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The amount to be received in the smallest unit of the currency (eg. cents) */ + fun receivingCurrencyAmount(receivingCurrencyAmount: Long) = apply { + body.receivingCurrencyAmount(receivingCurrencyAmount) + } + + /** + * Sets [Builder.receivingCurrencyAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.receivingCurrencyAmount] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receivingCurrencyAmount(receivingCurrencyAmount: JsonField) = apply { + body.receivingCurrencyAmount(receivingCurrencyAmount) + } + + /** The currency code for the receiving amount */ + fun receivingCurrencyCode(receivingCurrencyCode: String) = apply { + body.receivingCurrencyCode(receivingCurrencyCode) + } + + /** + * Sets [Builder.receivingCurrencyCode] to an arbitrary JSON value. + * + * You should usually call [Builder.receivingCurrencyCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receivingCurrencyCode(receivingCurrencyCode: JsonField) = apply { + body.receivingCurrencyCode(receivingCurrencyCode) + } + + /** UMA address of the sender from the sandbox */ + fun senderUmaAddress(senderUmaAddress: String) = apply { + body.senderUmaAddress(senderUmaAddress) + } + + /** + * Sets [Builder.senderUmaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.senderUmaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun senderUmaAddress(senderUmaAddress: JsonField) = apply { + body.senderUmaAddress(senderUmaAddress) + } + + /** System ID of the receiver (optional if receiverUmaAddress is provided) */ + fun customerId(customerId: String) = apply { body.customerId(customerId) } + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { body.customerId(customerId) } + + /** UMA address of the receiver (optional if customerId is provided) */ + fun receiverUmaAddress(receiverUmaAddress: String) = apply { + body.receiverUmaAddress(receiverUmaAddress) + } + + /** + * Sets [Builder.receiverUmaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.receiverUmaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receiverUmaAddress(receiverUmaAddress: JsonField) = apply { + body.receiverUmaAddress(receiverUmaAddress) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [UmaReceivePaymentParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .receivingCurrencyAmount() + * .receivingCurrencyCode() + * .senderUmaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaReceivePaymentParams = + UmaReceivePaymentParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val receivingCurrencyAmount: JsonField, + private val receivingCurrencyCode: JsonField, + private val senderUmaAddress: JsonField, + private val customerId: JsonField, + private val receiverUmaAddress: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("receivingCurrencyAmount") + @ExcludeMissing + receivingCurrencyAmount: JsonField = JsonMissing.of(), + @JsonProperty("receivingCurrencyCode") + @ExcludeMissing + receivingCurrencyCode: JsonField = JsonMissing.of(), + @JsonProperty("senderUmaAddress") + @ExcludeMissing + senderUmaAddress: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("receiverUmaAddress") + @ExcludeMissing + receiverUmaAddress: JsonField = JsonMissing.of(), + ) : this( + receivingCurrencyAmount, + receivingCurrencyCode, + senderUmaAddress, + customerId, + receiverUmaAddress, + mutableMapOf(), + ) + + /** + * The amount to be received in the smallest unit of the currency (eg. cents) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivingCurrencyAmount(): Long = + receivingCurrencyAmount.getRequired("receivingCurrencyAmount") + + /** + * The currency code for the receiving amount + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivingCurrencyCode(): String = + receivingCurrencyCode.getRequired("receivingCurrencyCode") + + /** + * UMA address of the sender from the sandbox + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun senderUmaAddress(): String = senderUmaAddress.getRequired("senderUmaAddress") + + /** + * System ID of the receiver (optional if receiverUmaAddress is provided) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun customerId(): String? = customerId.getNullable("customerId") + + /** + * UMA address of the receiver (optional if customerId is provided) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun receiverUmaAddress(): String? = receiverUmaAddress.getNullable("receiverUmaAddress") + + /** + * Returns the raw JSON value of [receivingCurrencyAmount]. + * + * Unlike [receivingCurrencyAmount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("receivingCurrencyAmount") + @ExcludeMissing + fun _receivingCurrencyAmount(): JsonField = receivingCurrencyAmount + + /** + * Returns the raw JSON value of [receivingCurrencyCode]. + * + * Unlike [receivingCurrencyCode], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("receivingCurrencyCode") + @ExcludeMissing + fun _receivingCurrencyCode(): JsonField = receivingCurrencyCode + + /** + * Returns the raw JSON value of [senderUmaAddress]. + * + * Unlike [senderUmaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("senderUmaAddress") + @ExcludeMissing + fun _senderUmaAddress(): JsonField = senderUmaAddress + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") + @ExcludeMissing + fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [receiverUmaAddress]. + * + * Unlike [receiverUmaAddress], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("receiverUmaAddress") + @ExcludeMissing + fun _receiverUmaAddress(): JsonField = receiverUmaAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .receivingCurrencyAmount() + * .receivingCurrencyCode() + * .senderUmaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var receivingCurrencyAmount: JsonField? = null + private var receivingCurrencyCode: JsonField? = null + private var senderUmaAddress: JsonField? = null + private var customerId: JsonField = JsonMissing.of() + private var receiverUmaAddress: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + receivingCurrencyAmount = body.receivingCurrencyAmount + receivingCurrencyCode = body.receivingCurrencyCode + senderUmaAddress = body.senderUmaAddress + customerId = body.customerId + receiverUmaAddress = body.receiverUmaAddress + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The amount to be received in the smallest unit of the currency (eg. cents) */ + fun receivingCurrencyAmount(receivingCurrencyAmount: Long) = + receivingCurrencyAmount(JsonField.of(receivingCurrencyAmount)) + + /** + * Sets [Builder.receivingCurrencyAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.receivingCurrencyAmount] with a well-typed [Long] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun receivingCurrencyAmount(receivingCurrencyAmount: JsonField) = apply { + this.receivingCurrencyAmount = receivingCurrencyAmount + } + + /** The currency code for the receiving amount */ + fun receivingCurrencyCode(receivingCurrencyCode: String) = + receivingCurrencyCode(JsonField.of(receivingCurrencyCode)) + + /** + * Sets [Builder.receivingCurrencyCode] to an arbitrary JSON value. + * + * You should usually call [Builder.receivingCurrencyCode] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun receivingCurrencyCode(receivingCurrencyCode: JsonField) = apply { + this.receivingCurrencyCode = receivingCurrencyCode + } + + /** UMA address of the sender from the sandbox */ + fun senderUmaAddress(senderUmaAddress: String) = + senderUmaAddress(JsonField.of(senderUmaAddress)) + + /** + * Sets [Builder.senderUmaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.senderUmaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun senderUmaAddress(senderUmaAddress: JsonField) = apply { + this.senderUmaAddress = senderUmaAddress + } + + /** System ID of the receiver (optional if receiverUmaAddress is provided) */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** UMA address of the receiver (optional if customerId is provided) */ + fun receiverUmaAddress(receiverUmaAddress: String) = + receiverUmaAddress(JsonField.of(receiverUmaAddress)) + + /** + * Sets [Builder.receiverUmaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.receiverUmaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receiverUmaAddress(receiverUmaAddress: JsonField) = apply { + this.receiverUmaAddress = receiverUmaAddress + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .receivingCurrencyAmount() + * .receivingCurrencyCode() + * .senderUmaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("receivingCurrencyAmount", receivingCurrencyAmount), + checkRequired("receivingCurrencyCode", receivingCurrencyCode), + checkRequired("senderUmaAddress", senderUmaAddress), + customerId, + receiverUmaAddress, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + receivingCurrencyAmount() + receivingCurrencyCode() + senderUmaAddress() + customerId() + receiverUmaAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (receivingCurrencyAmount.asKnown() == null) 0 else 1) + + (if (receivingCurrencyCode.asKnown() == null) 0 else 1) + + (if (senderUmaAddress.asKnown() == null) 0 else 1) + + (if (customerId.asKnown() == null) 0 else 1) + + (if (receiverUmaAddress.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + receivingCurrencyAmount == other.receivingCurrencyAmount && + receivingCurrencyCode == other.receivingCurrencyCode && + senderUmaAddress == other.senderUmaAddress && + customerId == other.customerId && + receiverUmaAddress == other.receiverUmaAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + receivingCurrencyAmount, + receivingCurrencyCode, + senderUmaAddress, + customerId, + receiverUmaAddress, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{receivingCurrencyAmount=$receivingCurrencyAmount, receivingCurrencyCode=$receivingCurrencyCode, senderUmaAddress=$senderUmaAddress, customerId=$customerId, receiverUmaAddress=$receiverUmaAddress, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaReceivePaymentParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "UmaReceivePaymentParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/ApiToken.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/ApiToken.kt new file mode 100644 index 00000000..de798616 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/ApiToken.kt @@ -0,0 +1,447 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class ApiToken +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val clientId: JsonField, + private val createdAt: JsonField, + private val name: JsonField, + private val permissions: JsonField>, + private val updatedAt: JsonField, + private val clientSecret: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("clientId") @ExcludeMissing clientId: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("permissions") + @ExcludeMissing + permissions: JsonField> = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("clientSecret") + @ExcludeMissing + clientSecret: JsonField = JsonMissing.of(), + ) : this(id, clientId, createdAt, name, permissions, updatedAt, clientSecret, mutableMapOf()) + + /** + * System-generated unique identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * An opaque identifier that should be used as a client_id (or username) in the HTTP Basic + * Authentication scheme when issuing http requests to Grid. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun clientId(): String = clientId.getRequired("clientId") + + /** + * Creation timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime = createdAt.getRequired("createdAt") + + /** + * Name of the token + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * A list of permissions granted to the token + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun permissions(): List = permissions.getRequired("permissions") + + /** + * Last update timestamp + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime = updatedAt.getRequired("updatedAt") + + /** + * The secret that should be used to authenticate against Grid API. This secret is not stored + * and will never be available again after creation. Platform must keep this secret secure as it + * grants access to the account. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun clientSecret(): String? = clientSecret.getNullable("clientSecret") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("clientId") @ExcludeMissing fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [permissions]. + * + * Unlike [permissions], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("permissions") + @ExcludeMissing + fun _permissions(): JsonField> = permissions + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [clientSecret]. + * + * Unlike [clientSecret], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("clientSecret") + @ExcludeMissing + fun _clientSecret(): JsonField = clientSecret + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ApiToken]. + * + * The following fields are required: + * ```kotlin + * .id() + * .clientId() + * .createdAt() + * .name() + * .permissions() + * .updatedAt() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ApiToken]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var clientId: JsonField? = null + private var createdAt: JsonField? = null + private var name: JsonField? = null + private var permissions: JsonField>? = null + private var updatedAt: JsonField? = null + private var clientSecret: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(apiToken: ApiToken) = apply { + id = apiToken.id + clientId = apiToken.clientId + createdAt = apiToken.createdAt + name = apiToken.name + permissions = apiToken.permissions.map { it.toMutableList() } + updatedAt = apiToken.updatedAt + clientSecret = apiToken.clientSecret + additionalProperties = apiToken.additionalProperties.toMutableMap() + } + + /** System-generated unique identifier */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** + * An opaque identifier that should be used as a client_id (or username) in the HTTP Basic + * Authentication scheme when issuing http requests to Grid. + */ + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun clientId(clientId: JsonField) = apply { this.clientId = clientId } + + /** Creation timestamp */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Name of the token */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** A list of permissions granted to the token */ + fun permissions(permissions: List) = permissions(JsonField.of(permissions)) + + /** + * Sets [Builder.permissions] to an arbitrary JSON value. + * + * You should usually call [Builder.permissions] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun permissions(permissions: JsonField>) = apply { + this.permissions = permissions.map { it.toMutableList() } + } + + /** + * Adds a single [Permission] to [permissions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addPermission(permission: Permission) = apply { + permissions = + (permissions ?: JsonField.of(mutableListOf())).also { + checkKnown("permissions", it).add(permission) + } + } + + /** Last update timestamp */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + /** + * The secret that should be used to authenticate against Grid API. This secret is not + * stored and will never be available again after creation. Platform must keep this secret + * secure as it grants access to the account. + */ + fun clientSecret(clientSecret: String) = clientSecret(JsonField.of(clientSecret)) + + /** + * Sets [Builder.clientSecret] to an arbitrary JSON value. + * + * You should usually call [Builder.clientSecret] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun clientSecret(clientSecret: JsonField) = apply { + this.clientSecret = clientSecret + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ApiToken]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .id() + * .clientId() + * .createdAt() + * .name() + * .permissions() + * .updatedAt() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiToken = + ApiToken( + checkRequired("id", id), + checkRequired("clientId", clientId), + checkRequired("createdAt", createdAt), + checkRequired("name", name), + checkRequired("permissions", permissions).map { it.toImmutable() }, + checkRequired("updatedAt", updatedAt), + clientSecret, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ApiToken = apply { + if (validated) { + return@apply + } + + id() + clientId() + createdAt() + name() + permissions().forEach { it.validate() } + updatedAt() + clientSecret() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (if (clientId.asKnown() == null) 0 else 1) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (name.asKnown() == null) 0 else 1) + + (permissions.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (if (clientSecret.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ApiToken && + id == other.id && + clientId == other.clientId && + createdAt == other.createdAt && + name == other.name && + permissions == other.permissions && + updatedAt == other.updatedAt && + clientSecret == other.clientSecret && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + clientId, + createdAt, + name, + permissions, + updatedAt, + clientSecret, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ApiToken{id=$id, clientId=$clientId, createdAt=$createdAt, name=$name, permissions=$permissions, updatedAt=$updatedAt, clientSecret=$clientSecret, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/Permission.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/Permission.kt new file mode 100644 index 00000000..bbdf2ffa --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/Permission.kt @@ -0,0 +1,141 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.errors.GridInvalidDataException + +/** + * Permission of an API token that determines what actions the token can perform: VIEW: Can view all + * data, including platform config, customers and transactions TRANSACT: Can send payments MANAGE: + * Can manage platform config, api tokens and customers + */ +class Permission @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val VIEW = of("VIEW") + + val TRANSACT = of("TRANSACT") + + val MANAGE = of("MANAGE") + + fun of(value: String) = Permission(JsonField.of(value)) + } + + /** An enum containing [Permission]'s known values. */ + enum class Known { + VIEW, + TRANSACT, + MANAGE, + } + + /** + * An enum containing [Permission]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Permission] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + VIEW, + TRANSACT, + MANAGE, + /** An enum member indicating that [Permission] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + VIEW -> Value.VIEW + TRANSACT -> Value.TRANSACT + MANAGE -> Value.MANAGE + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + VIEW -> Known.VIEW + TRANSACT -> Known.TRANSACT + MANAGE -> Known.MANAGE + else -> throw GridInvalidDataException("Unknown Permission: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): Permission = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Permission && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenCreateParams.kt new file mode 100644 index 00000000..46e1cb2c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenCreateParams.kt @@ -0,0 +1,513 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** Create a new API token to access the Grid APIs. */ +class TokenCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Name of the token to help identify it + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = body.name() + + /** + * A list of permissions to grant to the token + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun permissions(): List = body.permissions() + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _name(): JsonField = body._name() + + /** + * Returns the raw JSON value of [permissions]. + * + * Unlike [permissions], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _permissions(): JsonField> = body._permissions() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TokenCreateParams]. + * + * The following fields are required: + * ```kotlin + * .name() + * .permissions() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TokenCreateParams]. */ + class Builder internal constructor() { + + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(tokenCreateParams: TokenCreateParams) = apply { + body = tokenCreateParams.body.toBuilder() + additionalHeaders = tokenCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = tokenCreateParams.additionalQueryParams.toBuilder() + } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [name] + * - [permissions] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Name of the token to help identify it */ + fun name(name: String) = apply { body.name(name) } + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { body.name(name) } + + /** A list of permissions to grant to the token */ + fun permissions(permissions: List) = apply { body.permissions(permissions) } + + /** + * Sets [Builder.permissions] to an arbitrary JSON value. + * + * You should usually call [Builder.permissions] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun permissions(permissions: JsonField>) = apply { + body.permissions(permissions) + } + + /** + * Adds a single [Permission] to [permissions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addPermission(permission: Permission) = apply { body.addPermission(permission) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TokenCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .name() + * .permissions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TokenCreateParams = + TokenCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val name: JsonField, + private val permissions: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("permissions") + @ExcludeMissing + permissions: JsonField> = JsonMissing.of(), + ) : this(name, permissions, mutableMapOf()) + + /** + * Name of the token to help identify it + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun name(): String = name.getRequired("name") + + /** + * A list of permissions to grant to the token + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun permissions(): List = permissions.getRequired("permissions") + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [permissions]. + * + * Unlike [permissions], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("permissions") + @ExcludeMissing + fun _permissions(): JsonField> = permissions + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .name() + * .permissions() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var name: JsonField? = null + private var permissions: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + name = body.name + permissions = body.permissions.map { it.toMutableList() } + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Name of the token to help identify it */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** A list of permissions to grant to the token */ + fun permissions(permissions: List) = permissions(JsonField.of(permissions)) + + /** + * Sets [Builder.permissions] to an arbitrary JSON value. + * + * You should usually call [Builder.permissions] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun permissions(permissions: JsonField>) = apply { + this.permissions = permissions.map { it.toMutableList() } + } + + /** + * Adds a single [Permission] to [permissions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addPermission(permission: Permission) = apply { + permissions = + (permissions ?: JsonField.of(mutableListOf())).also { + checkKnown("permissions", it).add(permission) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .name() + * .permissions() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("name", name), + checkRequired("permissions", permissions).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + name() + permissions().forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (name.asKnown() == null) 0 else 1) + + (permissions.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + name == other.name && + permissions == other.permissions && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(name, permissions, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{name=$name, permissions=$permissions, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenCreateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TokenCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenDeleteParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenDeleteParams.kt new file mode 100644 index 00000000..162c799d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenDeleteParams.kt @@ -0,0 +1,222 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import java.util.Objects + +/** Delete an API token by their system-generated ID */ +class TokenDeleteParams +private constructor( + private val tokenId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun tokenId(): String? = tokenId + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TokenDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [TokenDeleteParams]. */ + fun builder() = Builder() + } + + /** A builder for [TokenDeleteParams]. */ + class Builder internal constructor() { + + private var tokenId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + internal fun from(tokenDeleteParams: TokenDeleteParams) = apply { + tokenId = tokenDeleteParams.tokenId + additionalHeaders = tokenDeleteParams.additionalHeaders.toBuilder() + additionalQueryParams = tokenDeleteParams.additionalQueryParams.toBuilder() + additionalBodyProperties = tokenDeleteParams.additionalBodyProperties.toMutableMap() + } + + fun tokenId(tokenId: String?) = apply { this.tokenId = tokenId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [TokenDeleteParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TokenDeleteParams = + TokenDeleteParams( + tokenId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Map? = additionalBodyProperties.ifEmpty { null } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> tokenId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenDeleteParams && + tokenId == other.tokenId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash(tokenId, additionalHeaders, additionalQueryParams, additionalBodyProperties) + + override fun toString() = + "TokenDeleteParams{tokenId=$tokenId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPage.kt new file mode 100644 index 00000000..af851fa8 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPage.kt @@ -0,0 +1,141 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.services.blocking.TokenService +import java.util.Objects + +/** @see TokenService.list */ +class TokenListPage +private constructor( + private val service: TokenService, + private val params: TokenListParams, + private val response: TokenListPageResponse, +) : Page { + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): TokenListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): TokenListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): TokenListParams = params + + /** The response that this page was parsed from. */ + fun response(): TokenListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TokenListPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TokenListPage]. */ + class Builder internal constructor() { + + private var service: TokenService? = null + private var params: TokenListParams? = null + private var response: TokenListPageResponse? = null + + internal fun from(tokenListPage: TokenListPage) = apply { + service = tokenListPage.service + params = tokenListPage.params + response = tokenListPage.response + } + + fun service(service: TokenService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: TokenListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: TokenListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [TokenListPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TokenListPage = + TokenListPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenListPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = "TokenListPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageAsync.kt new file mode 100644 index 00000000..9943a3b6 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageAsync.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.services.async.TokenServiceAsync +import java.util.Objects + +/** @see TokenServiceAsync.list */ +class TokenListPageAsync +private constructor( + private val service: TokenServiceAsync, + private val params: TokenListParams, + private val response: TokenListPageResponse, +) : PageAsync { + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [TokenListPageResponse], but gracefully handles missing data. + * + * @see TokenListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): TokenListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): TokenListPageAsync = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): TokenListParams = params + + /** The response that this page was parsed from. */ + fun response(): TokenListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TokenListPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TokenListPageAsync]. */ + class Builder internal constructor() { + + private var service: TokenServiceAsync? = null + private var params: TokenListParams? = null + private var response: TokenListPageResponse? = null + + internal fun from(tokenListPageAsync: TokenListPageAsync) = apply { + service = tokenListPageAsync.service + params = tokenListPageAsync.params + response = tokenListPageAsync.response + } + + fun service(service: TokenServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: TokenListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: TokenListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [TokenListPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TokenListPageAsync = + TokenListPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenListPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "TokenListPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageResponse.kt new file mode 100644 index 00000000..aed79423 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListPageResponse.kt @@ -0,0 +1,299 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class TokenListPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of tokens matching the filter criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun hasMore(): Boolean = hasMore.getRequired("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of tokens matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TokenListPageResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TokenListPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField? = null + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(tokenListPageResponse: TokenListPageResponse) = apply { + data = tokenListPageResponse.data.map { it.toMutableList() } + hasMore = tokenListPageResponse.hasMore + nextCursor = tokenListPageResponse.nextCursor + totalCount = tokenListPageResponse.totalCount + additionalProperties = tokenListPageResponse.additionalProperties.toMutableMap() + } + + /** List of tokens matching the filter criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [ApiToken] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: ApiToken) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of tokens matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [TokenListPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TokenListPageResponse = + TokenListPageResponse( + checkRequired("data", data).map { it.toImmutable() }, + checkRequired("hasMore", hasMore), + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): TokenListPageResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenListPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TokenListPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListParams.kt new file mode 100644 index 00000000..9eeb554c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenListParams.kt @@ -0,0 +1,296 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.util.Objects + +/** + * Retrieve a list of API tokens with optional filtering parameters. Returns all tokens that match + * the specified filters. If no filters are provided, returns all tokens (paginated). + */ +class TokenListParams +private constructor( + private val createdAfter: OffsetDateTime?, + private val createdBefore: OffsetDateTime?, + private val cursor: String?, + private val limit: Long?, + private val name: String?, + private val updatedAfter: OffsetDateTime?, + private val updatedBefore: OffsetDateTime?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Filter customers created after this timestamp (inclusive) */ + fun createdAfter(): OffsetDateTime? = createdAfter + + /** Filter customers created before this timestamp (inclusive) */ + fun createdBefore(): OffsetDateTime? = createdBefore + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Filter by name of the token */ + fun name(): String? = name + + /** Filter customers updated after this timestamp (inclusive) */ + fun updatedAfter(): OffsetDateTime? = updatedAfter + + /** Filter customers updated before this timestamp (inclusive) */ + fun updatedBefore(): OffsetDateTime? = updatedBefore + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TokenListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [TokenListParams]. */ + fun builder() = Builder() + } + + /** A builder for [TokenListParams]. */ + class Builder internal constructor() { + + private var createdAfter: OffsetDateTime? = null + private var createdBefore: OffsetDateTime? = null + private var cursor: String? = null + private var limit: Long? = null + private var name: String? = null + private var updatedAfter: OffsetDateTime? = null + private var updatedBefore: OffsetDateTime? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(tokenListParams: TokenListParams) = apply { + createdAfter = tokenListParams.createdAfter + createdBefore = tokenListParams.createdBefore + cursor = tokenListParams.cursor + limit = tokenListParams.limit + name = tokenListParams.name + updatedAfter = tokenListParams.updatedAfter + updatedBefore = tokenListParams.updatedBefore + additionalHeaders = tokenListParams.additionalHeaders.toBuilder() + additionalQueryParams = tokenListParams.additionalQueryParams.toBuilder() + } + + /** Filter customers created after this timestamp (inclusive) */ + fun createdAfter(createdAfter: OffsetDateTime?) = apply { this.createdAfter = createdAfter } + + /** Filter customers created before this timestamp (inclusive) */ + fun createdBefore(createdBefore: OffsetDateTime?) = apply { + this.createdBefore = createdBefore + } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Filter by name of the token */ + fun name(name: String?) = apply { this.name = name } + + /** Filter customers updated after this timestamp (inclusive) */ + fun updatedAfter(updatedAfter: OffsetDateTime?) = apply { this.updatedAfter = updatedAfter } + + /** Filter customers updated before this timestamp (inclusive) */ + fun updatedBefore(updatedBefore: OffsetDateTime?) = apply { + this.updatedBefore = updatedBefore + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TokenListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TokenListParams = + TokenListParams( + createdAfter, + createdBefore, + cursor, + limit, + name, + updatedAfter, + updatedBefore, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + createdAfter?.let { + put("createdAfter", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + createdBefore?.let { + put("createdBefore", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + cursor?.let { put("cursor", it) } + limit?.let { put("limit", it.toString()) } + name?.let { put("name", it) } + updatedAfter?.let { + put("updatedAfter", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + updatedBefore?.let { + put("updatedBefore", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + putAll(additionalQueryParams) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenListParams && + createdAfter == other.createdAfter && + createdBefore == other.createdBefore && + cursor == other.cursor && + limit == other.limit && + name == other.name && + updatedAfter == other.updatedAfter && + updatedBefore == other.updatedBefore && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + createdAfter, + createdBefore, + cursor, + limit, + name, + updatedAfter, + updatedBefore, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "TokenListParams{createdAfter=$createdAfter, createdBefore=$createdBefore, cursor=$cursor, limit=$limit, name=$name, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenRetrieveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenRetrieveParams.kt new file mode 100644 index 00000000..6fc32c91 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/tokens/TokenRetrieveParams.kt @@ -0,0 +1,183 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** Retrieve an API token by their system-generated ID */ +class TokenRetrieveParams +private constructor( + private val tokenId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun tokenId(): String? = tokenId + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TokenRetrieveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [TokenRetrieveParams]. */ + fun builder() = Builder() + } + + /** A builder for [TokenRetrieveParams]. */ + class Builder internal constructor() { + + private var tokenId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(tokenRetrieveParams: TokenRetrieveParams) = apply { + tokenId = tokenRetrieveParams.tokenId + additionalHeaders = tokenRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = tokenRetrieveParams.additionalQueryParams.toBuilder() + } + + fun tokenId(tokenId: String?) = apply { this.tokenId = tokenId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TokenRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TokenRetrieveParams = + TokenRetrieveParams(tokenId, additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> tokenId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TokenRetrieveParams && + tokenId == other.tokenId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(tokenId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TokenRetrieveParams{tokenId=$tokenId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/BaseTransactionSource.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/BaseTransactionSource.kt new file mode 100644 index 00000000..d7ef2274 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/BaseTransactionSource.kt @@ -0,0 +1,154 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class BaseTransactionSource +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val currency: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("currency") @ExcludeMissing currency: JsonField = JsonMissing.of() + ) : this(currency, mutableMapOf()) + + /** + * Currency code for the source + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [BaseTransactionSource]. */ + fun builder() = Builder() + } + + /** A builder for [BaseTransactionSource]. */ + class Builder internal constructor() { + + private var currency: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(baseTransactionSource: BaseTransactionSource) = apply { + currency = baseTransactionSource.currency + additionalProperties = baseTransactionSource.additionalProperties.toMutableMap() + } + + /** Currency code for the source */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BaseTransactionSource]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BaseTransactionSource = + BaseTransactionSource(currency, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): BaseTransactionSource = apply { + if (validated) { + return@apply + } + + currency() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (currency.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BaseTransactionSource && + currency == other.currency && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(currency, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BaseTransactionSource{currency=$currency, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/IncomingTransaction.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/IncomingTransaction.kt new file mode 100644 index 00000000..f981c436 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/IncomingTransaction.kt @@ -0,0 +1,1604 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.transferin.Transaction +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class IncomingTransaction +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val customerId: JsonField, + private val destination: JsonField, + private val platformCustomerId: JsonField, + private val status: JsonField, + private val type: JsonField, + private val counterpartyInformation: JsonField, + private val createdAt: JsonField, + private val description: JsonField, + private val settledAt: JsonField, + private val updatedAt: JsonField, + private val receivedAmount: JsonField, + private val failureReason: JsonField, + private val rateDetails: JsonField, + private val reconciliationInstructions: JsonField, + private val source: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("status") + @ExcludeMissing + status: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("counterpartyInformation") + @ExcludeMissing + counterpartyInformation: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("description") + @ExcludeMissing + description: JsonField = JsonMissing.of(), + @JsonProperty("settledAt") + @ExcludeMissing + settledAt: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("receivedAmount") + @ExcludeMissing + receivedAmount: JsonField = JsonMissing.of(), + @JsonProperty("failureReason") + @ExcludeMissing + failureReason: JsonField = JsonMissing.of(), + @JsonProperty("rateDetails") + @ExcludeMissing + rateDetails: JsonField = JsonMissing.of(), + @JsonProperty("reconciliationInstructions") + @ExcludeMissing + reconciliationInstructions: JsonField = JsonMissing.of(), + @JsonProperty("source") + @ExcludeMissing + source: JsonField = JsonMissing.of(), + ) : this( + id, + customerId, + destination, + platformCustomerId, + status, + type, + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + receivedAmount, + failureReason, + rateDetails, + reconciliationInstructions, + source, + mutableMapOf(), + ) + + fun toTransaction(): Transaction = + Transaction.builder() + .id(id) + .customerId(customerId) + .destination(destination) + .platformCustomerId(platformCustomerId) + .status(status) + .type(type) + .counterpartyInformation(counterpartyInformation) + .createdAt(createdAt) + .description(description) + .settledAt(settledAt) + .updatedAt(updatedAt) + .build() + + /** + * Unique identifier for the transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * System ID of the customer (sender for outgoing, recipient for incoming) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerId(): String = customerId.getRequired("customerId") + + /** + * Destination account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Transaction.Destination = destination.getRequired("destination") + + /** + * Platform-specific ID of the customer (sender for outgoing, recipient for incoming) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Status of a payment transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): TransactionStatus = status.getRequired("status") + + /** + * Type of transaction (incoming payment or outgoing payment) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): TransactionType = type.getRequired("type") + + /** + * Additional information about the counterparty, if available and relevant to the transaction + * and platform. Only applicable for transactions to/from UMA addresses. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun counterpartyInformation(): Transaction.CounterpartyInformation? = + counterpartyInformation.getNullable("counterpartyInformation") + + /** + * When the transaction was created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Optional memo or description for the payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun description(): String? = description.getNullable("description") + + /** + * When the payment was or will be settled + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun settledAt(): OffsetDateTime? = settledAt.getNullable("settledAt") + + /** + * When the transaction was last updated + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * Amount received in the recipient's currency + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun receivedAmount(): CurrencyAmount = receivedAmount.getRequired("receivedAmount") + + /** + * If the transaction failed, this field provides the reason for failure. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun failureReason(): FailureReason? = failureReason.getNullable("failureReason") + + /** + * Details about the rate and fees for the transaction. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun rateDetails(): RateDetails? = rateDetails.getNullable("rateDetails") + + /** + * Included for all transactions except those with "CREATED" status + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun reconciliationInstructions(): ReconciliationInstructions? = + reconciliationInstructions.getNullable("reconciliationInstructions") + + /** + * Source account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun source(): TransactionSourceOneOf? = source.getNullable("source") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + /** + * Returns the raw JSON value of [counterpartyInformation]. + * + * Unlike [counterpartyInformation], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("counterpartyInformation") + @ExcludeMissing + fun _counterpartyInformation(): JsonField = + counterpartyInformation + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [settledAt]. + * + * Unlike [settledAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("settledAt") + @ExcludeMissing + fun _settledAt(): JsonField = settledAt + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + /** + * Returns the raw JSON value of [receivedAmount]. + * + * Unlike [receivedAmount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("receivedAmount") + @ExcludeMissing + fun _receivedAmount(): JsonField = receivedAmount + + /** + * Returns the raw JSON value of [failureReason]. + * + * Unlike [failureReason], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("failureReason") + @ExcludeMissing + fun _failureReason(): JsonField = failureReason + + /** + * Returns the raw JSON value of [rateDetails]. + * + * Unlike [rateDetails], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("rateDetails") + @ExcludeMissing + fun _rateDetails(): JsonField = rateDetails + + /** + * Returns the raw JSON value of [reconciliationInstructions]. + * + * Unlike [reconciliationInstructions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("reconciliationInstructions") + @ExcludeMissing + fun _reconciliationInstructions(): JsonField = + reconciliationInstructions + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") + @ExcludeMissing + fun _source(): JsonField = source + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [IncomingTransaction]. + * + * The following fields are required: + * ```kotlin + * .id() + * .customerId() + * .destination() + * .platformCustomerId() + * .status() + * .type() + * .receivedAmount() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [IncomingTransaction]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var customerId: JsonField? = null + private var destination: JsonField? = null + private var platformCustomerId: JsonField? = null + private var status: JsonField? = null + private var type: JsonField? = null + private var counterpartyInformation: JsonField = + JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var description: JsonField = JsonMissing.of() + private var settledAt: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var receivedAmount: JsonField? = null + private var failureReason: JsonField = JsonMissing.of() + private var rateDetails: JsonField = JsonMissing.of() + private var reconciliationInstructions: JsonField = + JsonMissing.of() + private var source: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(incomingTransaction: IncomingTransaction) = apply { + id = incomingTransaction.id + customerId = incomingTransaction.customerId + destination = incomingTransaction.destination + platformCustomerId = incomingTransaction.platformCustomerId + status = incomingTransaction.status + type = incomingTransaction.type + counterpartyInformation = incomingTransaction.counterpartyInformation + createdAt = incomingTransaction.createdAt + description = incomingTransaction.description + settledAt = incomingTransaction.settledAt + updatedAt = incomingTransaction.updatedAt + receivedAmount = incomingTransaction.receivedAmount + failureReason = incomingTransaction.failureReason + rateDetails = incomingTransaction.rateDetails + reconciliationInstructions = incomingTransaction.reconciliationInstructions + source = incomingTransaction.source + additionalProperties = incomingTransaction.additionalProperties.toMutableMap() + } + + /** Unique identifier for the transaction */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** System ID of the customer (sender for outgoing, recipient for incoming) */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** Destination account details */ + fun destination(destination: Transaction.Destination) = + destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Transaction.Destination] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Alias for calling [destination] with `Transaction.Destination.ofAccount(account)`. */ + fun destination(account: Transaction.Destination.Account) = + destination(Transaction.Destination.ofAccount(account)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * Transaction.Destination.Account.builder() + * .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountDestination(accountId: String) = + destination( + Transaction.Destination.Account.builder() + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** + * Alias for calling [destination] with `Transaction.Destination.ofUmaAddress(umaAddress)`. + */ + fun destination(umaAddress: Transaction.Destination.UmaAddress) = + destination(Transaction.Destination.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * Transaction.Destination.UmaAddress.builder() + * .destinationType(Transaction.Destination.UmaAddress.DestinationType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressDestination(umaAddress: String) = + destination( + Transaction.Destination.UmaAddress.builder() + .destinationType(Transaction.Destination.UmaAddress.DestinationType.UMA_ADDRESS) + .umaAddress(umaAddress) + .build() + ) + + /** Platform-specific ID of the customer (sender for outgoing, recipient for incoming) */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** Status of a payment transaction */ + fun status(status: TransactionStatus) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [TransactionStatus] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** Type of transaction (incoming payment or outgoing payment) */ + fun type(type: TransactionType) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [TransactionType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + /** + * Additional information about the counterparty, if available and relevant to the + * transaction and platform. Only applicable for transactions to/from UMA addresses. + */ + fun counterpartyInformation(counterpartyInformation: Transaction.CounterpartyInformation) = + counterpartyInformation(JsonField.of(counterpartyInformation)) + + /** + * Sets [Builder.counterpartyInformation] to an arbitrary JSON value. + * + * You should usually call [Builder.counterpartyInformation] with a well-typed + * [Transaction.CounterpartyInformation] value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun counterpartyInformation( + counterpartyInformation: JsonField + ) = apply { this.counterpartyInformation = counterpartyInformation } + + /** When the transaction was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Optional memo or description for the payment */ + fun description(description: String) = description(JsonField.of(description)) + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun description(description: JsonField) = apply { this.description = description } + + /** When the payment was or will be settled */ + fun settledAt(settledAt: OffsetDateTime) = settledAt(JsonField.of(settledAt)) + + /** + * Sets [Builder.settledAt] to an arbitrary JSON value. + * + * You should usually call [Builder.settledAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun settledAt(settledAt: JsonField) = apply { this.settledAt = settledAt } + + /** When the transaction was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + /** Amount received in the recipient's currency */ + fun receivedAmount(receivedAmount: CurrencyAmount) = + receivedAmount(JsonField.of(receivedAmount)) + + /** + * Sets [Builder.receivedAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.receivedAmount] with a well-typed [CurrencyAmount] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun receivedAmount(receivedAmount: JsonField) = apply { + this.receivedAmount = receivedAmount + } + + /** If the transaction failed, this field provides the reason for failure. */ + fun failureReason(failureReason: FailureReason) = failureReason(JsonField.of(failureReason)) + + /** + * Sets [Builder.failureReason] to an arbitrary JSON value. + * + * You should usually call [Builder.failureReason] with a well-typed [FailureReason] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun failureReason(failureReason: JsonField) = apply { + this.failureReason = failureReason + } + + /** Details about the rate and fees for the transaction. */ + fun rateDetails(rateDetails: RateDetails) = rateDetails(JsonField.of(rateDetails)) + + /** + * Sets [Builder.rateDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.rateDetails] with a well-typed [RateDetails] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun rateDetails(rateDetails: JsonField) = apply { + this.rateDetails = rateDetails + } + + /** Included for all transactions except those with "CREATED" status */ + fun reconciliationInstructions(reconciliationInstructions: ReconciliationInstructions) = + reconciliationInstructions(JsonField.of(reconciliationInstructions)) + + /** + * Sets [Builder.reconciliationInstructions] to an arbitrary JSON value. + * + * You should usually call [Builder.reconciliationInstructions] with a well-typed + * [ReconciliationInstructions] value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun reconciliationInstructions( + reconciliationInstructions: JsonField + ) = apply { this.reconciliationInstructions = reconciliationInstructions } + + /** Source account details */ + fun source(source: TransactionSourceOneOf) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [TransactionSourceOneOf] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** Alias for calling [source] with `TransactionSourceOneOf.ofAccount(account)`. */ + fun source(account: TransactionSourceOneOf.Account) = + source(TransactionSourceOneOf.ofAccount(account)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * TransactionSourceOneOf.Account.builder() + * .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountSource(accountId: String) = + source( + TransactionSourceOneOf.Account.builder() + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** Alias for calling [source] with `TransactionSourceOneOf.ofUmaAddress(umaAddress)`. */ + fun source(umaAddress: TransactionSourceOneOf.UmaAddress) = + source(TransactionSourceOneOf.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [source] with the following: + * ```kotlin + * TransactionSourceOneOf.UmaAddress.builder() + * .sourceType(TransactionSourceOneOf.UmaAddress.SourceType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressSource(umaAddress: String) = + source( + TransactionSourceOneOf.UmaAddress.builder() + .sourceType(TransactionSourceOneOf.UmaAddress.SourceType.UMA_ADDRESS) + .umaAddress(umaAddress) + .build() + ) + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [IncomingTransaction]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .id() + * .customerId() + * .destination() + * .platformCustomerId() + * .status() + * .type() + * .receivedAmount() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): IncomingTransaction = + IncomingTransaction( + checkRequired("id", id), + checkRequired("customerId", customerId), + checkRequired("destination", destination), + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("status", status), + checkRequired("type", type), + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + checkRequired("receivedAmount", receivedAmount), + failureReason, + rateDetails, + reconciliationInstructions, + source, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): IncomingTransaction = apply { + if (validated) { + return@apply + } + + id() + customerId() + destination().validate() + platformCustomerId() + status().validate() + type().validate() + counterpartyInformation()?.validate() + createdAt() + description() + settledAt() + updatedAt() + receivedAmount().validate() + failureReason()?.validate() + rateDetails()?.validate() + reconciliationInstructions()?.validate() + source()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (if (customerId.asKnown() == null) 0 else 1) + + (destination.asKnown()?.validity() ?: 0) + + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (status.asKnown()?.validity() ?: 0) + + (type.asKnown()?.validity() ?: 0) + + (counterpartyInformation.asKnown()?.validity() ?: 0) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (description.asKnown() == null) 0 else 1) + + (if (settledAt.asKnown() == null) 0 else 1) + + (if (updatedAt.asKnown() == null) 0 else 1) + + (receivedAmount.asKnown()?.validity() ?: 0) + + (failureReason.asKnown()?.validity() ?: 0) + + (rateDetails.asKnown()?.validity() ?: 0) + + (reconciliationInstructions.asKnown()?.validity() ?: 0) + + (source.asKnown()?.validity() ?: 0) + + /** If the transaction failed, this field provides the reason for failure. */ + class FailureReason @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val LNURLP_FAILED = of("LNURLP_FAILED") + + val PAY_REQUEST_FAILED = of("PAY_REQUEST_FAILED") + + val PAYMENT_APPROVAL_WEBHOOK_ERROR = of("PAYMENT_APPROVAL_WEBHOOK_ERROR") + + val PAYMENT_APPROVAL_TIMED_OUT = of("PAYMENT_APPROVAL_TIMED_OUT") + + val OFFRAMP_FAILED = of("OFFRAMP_FAILED") + + val MISSING_MANDATORY_PAYEE_DATA = of("MISSING_MANDATORY_PAYEE_DATA") + + val QUOTE_EXPIRED = of("QUOTE_EXPIRED") + + val QUOTE_EXECUTION_FAILED = of("QUOTE_EXECUTION_FAILED") + + fun of(value: String) = FailureReason(JsonField.of(value)) + } + + /** An enum containing [FailureReason]'s known values. */ + enum class Known { + LNURLP_FAILED, + PAY_REQUEST_FAILED, + PAYMENT_APPROVAL_WEBHOOK_ERROR, + PAYMENT_APPROVAL_TIMED_OUT, + OFFRAMP_FAILED, + MISSING_MANDATORY_PAYEE_DATA, + QUOTE_EXPIRED, + QUOTE_EXECUTION_FAILED, + } + + /** + * An enum containing [FailureReason]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [FailureReason] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + LNURLP_FAILED, + PAY_REQUEST_FAILED, + PAYMENT_APPROVAL_WEBHOOK_ERROR, + PAYMENT_APPROVAL_TIMED_OUT, + OFFRAMP_FAILED, + MISSING_MANDATORY_PAYEE_DATA, + QUOTE_EXPIRED, + QUOTE_EXECUTION_FAILED, + /** + * An enum member indicating that [FailureReason] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + LNURLP_FAILED -> Value.LNURLP_FAILED + PAY_REQUEST_FAILED -> Value.PAY_REQUEST_FAILED + PAYMENT_APPROVAL_WEBHOOK_ERROR -> Value.PAYMENT_APPROVAL_WEBHOOK_ERROR + PAYMENT_APPROVAL_TIMED_OUT -> Value.PAYMENT_APPROVAL_TIMED_OUT + OFFRAMP_FAILED -> Value.OFFRAMP_FAILED + MISSING_MANDATORY_PAYEE_DATA -> Value.MISSING_MANDATORY_PAYEE_DATA + QUOTE_EXPIRED -> Value.QUOTE_EXPIRED + QUOTE_EXECUTION_FAILED -> Value.QUOTE_EXECUTION_FAILED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + LNURLP_FAILED -> Known.LNURLP_FAILED + PAY_REQUEST_FAILED -> Known.PAY_REQUEST_FAILED + PAYMENT_APPROVAL_WEBHOOK_ERROR -> Known.PAYMENT_APPROVAL_WEBHOOK_ERROR + PAYMENT_APPROVAL_TIMED_OUT -> Known.PAYMENT_APPROVAL_TIMED_OUT + OFFRAMP_FAILED -> Known.OFFRAMP_FAILED + MISSING_MANDATORY_PAYEE_DATA -> Known.MISSING_MANDATORY_PAYEE_DATA + QUOTE_EXPIRED -> Known.QUOTE_EXPIRED + QUOTE_EXECUTION_FAILED -> Known.QUOTE_EXECUTION_FAILED + else -> throw GridInvalidDataException("Unknown FailureReason: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): FailureReason = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FailureReason && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Details about the rate and fees for the transaction. */ + class RateDetails + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val gridApiFixedFee: JsonField, + private val gridApiMultiplier: JsonField, + private val gridApiVariableFeeAmount: JsonField, + private val gridApiVariableFeeRate: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("gridApiFixedFee") + @ExcludeMissing + gridApiFixedFee: JsonField = JsonMissing.of(), + @JsonProperty("gridApiMultiplier") + @ExcludeMissing + gridApiMultiplier: JsonField = JsonMissing.of(), + @JsonProperty("gridApiVariableFeeAmount") + @ExcludeMissing + gridApiVariableFeeAmount: JsonField = JsonMissing.of(), + @JsonProperty("gridApiVariableFeeRate") + @ExcludeMissing + gridApiVariableFeeRate: JsonField = JsonMissing.of(), + ) : this( + gridApiFixedFee, + gridApiMultiplier, + gridApiVariableFeeAmount, + gridApiVariableFeeRate, + mutableMapOf(), + ) + + /** + * The fixed fee charged by the Grid product to execute the quote in the smallest unit of + * the receiving currency (eg. cents). + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiFixedFee(): Long = gridApiFixedFee.getRequired("gridApiFixedFee") + + /** + * The underlying multiplier from the mSATS to the receiving currency, including variable + * fees. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiMultiplier(): Double = gridApiMultiplier.getRequired("gridApiMultiplier") + + /** + * The variable fee amount charged by the Grid product to execute the quote in the smallest + * unit of the receiving currency (eg. cents). This is the receiving amount times + * gridApiVariableFeeRate. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiVariableFeeAmount(): Double = + gridApiVariableFeeAmount.getRequired("gridApiVariableFeeAmount") + + /** + * The variable fee rate charged by the Grid product to execute the quote as a percentage of + * the receiving currency amount. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun gridApiVariableFeeRate(): Double = + gridApiVariableFeeRate.getRequired("gridApiVariableFeeRate") + + /** + * Returns the raw JSON value of [gridApiFixedFee]. + * + * Unlike [gridApiFixedFee], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("gridApiFixedFee") + @ExcludeMissing + fun _gridApiFixedFee(): JsonField = gridApiFixedFee + + /** + * Returns the raw JSON value of [gridApiMultiplier]. + * + * Unlike [gridApiMultiplier], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("gridApiMultiplier") + @ExcludeMissing + fun _gridApiMultiplier(): JsonField = gridApiMultiplier + + /** + * Returns the raw JSON value of [gridApiVariableFeeAmount]. + * + * Unlike [gridApiVariableFeeAmount], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("gridApiVariableFeeAmount") + @ExcludeMissing + fun _gridApiVariableFeeAmount(): JsonField = gridApiVariableFeeAmount + + /** + * Returns the raw JSON value of [gridApiVariableFeeRate]. + * + * Unlike [gridApiVariableFeeRate], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("gridApiVariableFeeRate") + @ExcludeMissing + fun _gridApiVariableFeeRate(): JsonField = gridApiVariableFeeRate + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RateDetails]. + * + * The following fields are required: + * ```kotlin + * .gridApiFixedFee() + * .gridApiMultiplier() + * .gridApiVariableFeeAmount() + * .gridApiVariableFeeRate() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [RateDetails]. */ + class Builder internal constructor() { + + private var gridApiFixedFee: JsonField? = null + private var gridApiMultiplier: JsonField? = null + private var gridApiVariableFeeAmount: JsonField? = null + private var gridApiVariableFeeRate: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(rateDetails: RateDetails) = apply { + gridApiFixedFee = rateDetails.gridApiFixedFee + gridApiMultiplier = rateDetails.gridApiMultiplier + gridApiVariableFeeAmount = rateDetails.gridApiVariableFeeAmount + gridApiVariableFeeRate = rateDetails.gridApiVariableFeeRate + additionalProperties = rateDetails.additionalProperties.toMutableMap() + } + + /** + * The fixed fee charged by the Grid product to execute the quote in the smallest unit + * of the receiving currency (eg. cents). + */ + fun gridApiFixedFee(gridApiFixedFee: Long) = + gridApiFixedFee(JsonField.of(gridApiFixedFee)) + + /** + * Sets [Builder.gridApiFixedFee] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiFixedFee] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gridApiFixedFee(gridApiFixedFee: JsonField) = apply { + this.gridApiFixedFee = gridApiFixedFee + } + + /** + * The underlying multiplier from the mSATS to the receiving currency, including + * variable fees. + */ + fun gridApiMultiplier(gridApiMultiplier: Double) = + gridApiMultiplier(JsonField.of(gridApiMultiplier)) + + /** + * Sets [Builder.gridApiMultiplier] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiMultiplier] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gridApiMultiplier(gridApiMultiplier: JsonField) = apply { + this.gridApiMultiplier = gridApiMultiplier + } + + /** + * The variable fee amount charged by the Grid product to execute the quote in the + * smallest unit of the receiving currency (eg. cents). This is the receiving amount + * times gridApiVariableFeeRate. + */ + fun gridApiVariableFeeAmount(gridApiVariableFeeAmount: Double) = + gridApiVariableFeeAmount(JsonField.of(gridApiVariableFeeAmount)) + + /** + * Sets [Builder.gridApiVariableFeeAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiVariableFeeAmount] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun gridApiVariableFeeAmount(gridApiVariableFeeAmount: JsonField) = apply { + this.gridApiVariableFeeAmount = gridApiVariableFeeAmount + } + + /** + * The variable fee rate charged by the Grid product to execute the quote as a + * percentage of the receiving currency amount. + */ + fun gridApiVariableFeeRate(gridApiVariableFeeRate: Double) = + gridApiVariableFeeRate(JsonField.of(gridApiVariableFeeRate)) + + /** + * Sets [Builder.gridApiVariableFeeRate] to an arbitrary JSON value. + * + * You should usually call [Builder.gridApiVariableFeeRate] with a well-typed [Double] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun gridApiVariableFeeRate(gridApiVariableFeeRate: JsonField) = apply { + this.gridApiVariableFeeRate = gridApiVariableFeeRate + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [RateDetails]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .gridApiFixedFee() + * .gridApiMultiplier() + * .gridApiVariableFeeAmount() + * .gridApiVariableFeeRate() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RateDetails = + RateDetails( + checkRequired("gridApiFixedFee", gridApiFixedFee), + checkRequired("gridApiMultiplier", gridApiMultiplier), + checkRequired("gridApiVariableFeeAmount", gridApiVariableFeeAmount), + checkRequired("gridApiVariableFeeRate", gridApiVariableFeeRate), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): RateDetails = apply { + if (validated) { + return@apply + } + + gridApiFixedFee() + gridApiMultiplier() + gridApiVariableFeeAmount() + gridApiVariableFeeRate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (gridApiFixedFee.asKnown() == null) 0 else 1) + + (if (gridApiMultiplier.asKnown() == null) 0 else 1) + + (if (gridApiVariableFeeAmount.asKnown() == null) 0 else 1) + + (if (gridApiVariableFeeRate.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is RateDetails && + gridApiFixedFee == other.gridApiFixedFee && + gridApiMultiplier == other.gridApiMultiplier && + gridApiVariableFeeAmount == other.gridApiVariableFeeAmount && + gridApiVariableFeeRate == other.gridApiVariableFeeRate && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + gridApiFixedFee, + gridApiMultiplier, + gridApiVariableFeeAmount, + gridApiVariableFeeRate, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "RateDetails{gridApiFixedFee=$gridApiFixedFee, gridApiMultiplier=$gridApiMultiplier, gridApiVariableFeeAmount=$gridApiVariableFeeAmount, gridApiVariableFeeRate=$gridApiVariableFeeRate, additionalProperties=$additionalProperties}" + } + + /** Included for all transactions except those with "CREATED" status */ + class ReconciliationInstructions + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val reference: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of() + ) : this(reference, mutableMapOf()) + + /** + * Unique reference code that must be included with the payment to match it with the correct + * incoming transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun reference(): String = reference.getRequired("reference") + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [ReconciliationInstructions]. + * + * The following fields are required: + * ```kotlin + * .reference() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [ReconciliationInstructions]. */ + class Builder internal constructor() { + + private var reference: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(reconciliationInstructions: ReconciliationInstructions) = apply { + reference = reconciliationInstructions.reference + additionalProperties = + reconciliationInstructions.additionalProperties.toMutableMap() + } + + /** + * Unique reference code that must be included with the payment to match it with the + * correct incoming transaction + */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ReconciliationInstructions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .reference() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ReconciliationInstructions = + ReconciliationInstructions( + checkRequired("reference", reference), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ReconciliationInstructions = apply { + if (validated) { + return@apply + } + + reference() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (reference.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ReconciliationInstructions && + reference == other.reference && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(reference, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ReconciliationInstructions{reference=$reference, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is IncomingTransaction && + id == other.id && + customerId == other.customerId && + destination == other.destination && + platformCustomerId == other.platformCustomerId && + status == other.status && + type == other.type && + counterpartyInformation == other.counterpartyInformation && + createdAt == other.createdAt && + description == other.description && + settledAt == other.settledAt && + updatedAt == other.updatedAt && + receivedAmount == other.receivedAmount && + failureReason == other.failureReason && + rateDetails == other.rateDetails && + reconciliationInstructions == other.reconciliationInstructions && + source == other.source && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + customerId, + destination, + platformCustomerId, + status, + type, + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + receivedAmount, + failureReason, + rateDetails, + reconciliationInstructions, + source, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "IncomingTransaction{id=$id, customerId=$customerId, destination=$destination, platformCustomerId=$platformCustomerId, status=$status, type=$type, counterpartyInformation=$counterpartyInformation, createdAt=$createdAt, description=$description, settledAt=$settledAt, updatedAt=$updatedAt, receivedAmount=$receivedAmount, failureReason=$failureReason, rateDetails=$rateDetails, reconciliationInstructions=$reconciliationInstructions, source=$source, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionApproveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionApproveParams.kt new file mode 100644 index 00000000..f2b592de --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionApproveParams.kt @@ -0,0 +1,534 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Approve a pending incoming payment that was previously acknowledged with a 202 response. This + * endpoint allows platforms to asynchronously approve payments after async processing. + */ +class TransactionApproveParams +private constructor( + private val transactionId: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun transactionId(): String? = transactionId + + /** + * Information about the recipient, provided by the platform if requested in the original + * webhook via `requestedReceiverCustomerInfoFields`. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun receiverCustomerInfo(): ReceiverCustomerInfo? = body.receiverCustomerInfo() + + /** + * Returns the raw JSON value of [receiverCustomerInfo]. + * + * Unlike [receiverCustomerInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _receiverCustomerInfo(): JsonField = body._receiverCustomerInfo() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TransactionApproveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [TransactionApproveParams]. */ + fun builder() = Builder() + } + + /** A builder for [TransactionApproveParams]. */ + class Builder internal constructor() { + + private var transactionId: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(transactionApproveParams: TransactionApproveParams) = apply { + transactionId = transactionApproveParams.transactionId + body = transactionApproveParams.body.toBuilder() + additionalHeaders = transactionApproveParams.additionalHeaders.toBuilder() + additionalQueryParams = transactionApproveParams.additionalQueryParams.toBuilder() + } + + fun transactionId(transactionId: String?) = apply { this.transactionId = transactionId } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [receiverCustomerInfo] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * Information about the recipient, provided by the platform if requested in the original + * webhook via `requestedReceiverCustomerInfoFields`. + */ + fun receiverCustomerInfo(receiverCustomerInfo: ReceiverCustomerInfo) = apply { + body.receiverCustomerInfo(receiverCustomerInfo) + } + + /** + * Sets [Builder.receiverCustomerInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.receiverCustomerInfo] with a well-typed + * [ReceiverCustomerInfo] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun receiverCustomerInfo(receiverCustomerInfo: JsonField) = apply { + body.receiverCustomerInfo(receiverCustomerInfo) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TransactionApproveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TransactionApproveParams = + TransactionApproveParams( + transactionId, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> transactionId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val receiverCustomerInfo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("receiverCustomerInfo") + @ExcludeMissing + receiverCustomerInfo: JsonField = JsonMissing.of() + ) : this(receiverCustomerInfo, mutableMapOf()) + + /** + * Information about the recipient, provided by the platform if requested in the original + * webhook via `requestedReceiverCustomerInfoFields`. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun receiverCustomerInfo(): ReceiverCustomerInfo? = + receiverCustomerInfo.getNullable("receiverCustomerInfo") + + /** + * Returns the raw JSON value of [receiverCustomerInfo]. + * + * Unlike [receiverCustomerInfo], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("receiverCustomerInfo") + @ExcludeMissing + fun _receiverCustomerInfo(): JsonField = receiverCustomerInfo + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Body]. */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var receiverCustomerInfo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + receiverCustomerInfo = body.receiverCustomerInfo + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * Information about the recipient, provided by the platform if requested in the + * original webhook via `requestedReceiverCustomerInfoFields`. + */ + fun receiverCustomerInfo(receiverCustomerInfo: ReceiverCustomerInfo) = + receiverCustomerInfo(JsonField.of(receiverCustomerInfo)) + + /** + * Sets [Builder.receiverCustomerInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.receiverCustomerInfo] with a well-typed + * [ReceiverCustomerInfo] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun receiverCustomerInfo(receiverCustomerInfo: JsonField) = + apply { + this.receiverCustomerInfo = receiverCustomerInfo + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Body = Body(receiverCustomerInfo, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + receiverCustomerInfo()?.validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (receiverCustomerInfo.asKnown()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + receiverCustomerInfo == other.receiverCustomerInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(receiverCustomerInfo, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{receiverCustomerInfo=$receiverCustomerInfo, additionalProperties=$additionalProperties}" + } + + /** + * Information about the recipient, provided by the platform if requested in the original + * webhook via `requestedReceiverCustomerInfoFields`. + */ + class ReceiverCustomerInfo + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [ReceiverCustomerInfo]. */ + fun builder() = Builder() + } + + /** A builder for [ReceiverCustomerInfo]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(receiverCustomerInfo: ReceiverCustomerInfo) = apply { + additionalProperties = receiverCustomerInfo.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [ReceiverCustomerInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ReceiverCustomerInfo = + ReceiverCustomerInfo(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): ReceiverCustomerInfo = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ReceiverCustomerInfo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "ReceiverCustomerInfo{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionApproveParams && + transactionId == other.transactionId && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(transactionId, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TransactionApproveParams{transactionId=$transactionId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPage.kt new file mode 100644 index 00000000..eae731d3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPage.kt @@ -0,0 +1,143 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.models.transferin.Transaction +import com.grid.api.services.blocking.TransactionService +import java.util.Objects + +/** @see TransactionService.list */ +class TransactionListPage +private constructor( + private val service: TransactionService, + private val params: TransactionListParams, + private val response: TransactionListPageResponse, +) : Page { + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): TransactionListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): TransactionListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): TransactionListParams = params + + /** The response that this page was parsed from. */ + fun response(): TransactionListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TransactionListPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TransactionListPage]. */ + class Builder internal constructor() { + + private var service: TransactionService? = null + private var params: TransactionListParams? = null + private var response: TransactionListPageResponse? = null + + internal fun from(transactionListPage: TransactionListPage) = apply { + service = transactionListPage.service + params = transactionListPage.params + response = transactionListPage.response + } + + fun service(service: TransactionService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: TransactionListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: TransactionListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [TransactionListPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TransactionListPage = + TransactionListPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionListPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "TransactionListPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageAsync.kt new file mode 100644 index 00000000..113bd69c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageAsync.kt @@ -0,0 +1,143 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.models.transferin.Transaction +import com.grid.api.services.async.TransactionServiceAsync +import java.util.Objects + +/** @see TransactionServiceAsync.list */ +class TransactionListPageAsync +private constructor( + private val service: TransactionServiceAsync, + private val params: TransactionListParams, + private val response: TransactionListPageResponse, +) : PageAsync { + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [TransactionListPageResponse], but gracefully handles missing data. + * + * @see TransactionListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): TransactionListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): TransactionListPageAsync = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): TransactionListParams = params + + /** The response that this page was parsed from. */ + fun response(): TransactionListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TransactionListPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TransactionListPageAsync]. */ + class Builder internal constructor() { + + private var service: TransactionServiceAsync? = null + private var params: TransactionListParams? = null + private var response: TransactionListPageResponse? = null + + internal fun from(transactionListPageAsync: TransactionListPageAsync) = apply { + service = transactionListPageAsync.service + params = transactionListPageAsync.params + response = transactionListPageAsync.response + } + + fun service(service: TransactionServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: TransactionListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: TransactionListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [TransactionListPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TransactionListPageAsync = + TransactionListPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionListPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "TransactionListPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageResponse.kt new file mode 100644 index 00000000..2aa7b996 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListPageResponse.kt @@ -0,0 +1,300 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.checkRequired +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.transferin.Transaction +import java.util.Collections +import java.util.Objects + +class TransactionListPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of transactions matching the criteria + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun data(): List = data.getRequired("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun hasMore(): Boolean = hasMore.getRequired("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of transactions matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TransactionListPageResponse]. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TransactionListPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField? = null + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(transactionListPageResponse: TransactionListPageResponse) = apply { + data = transactionListPageResponse.data.map { it.toMutableList() } + hasMore = transactionListPageResponse.hasMore + nextCursor = transactionListPageResponse.nextCursor + totalCount = transactionListPageResponse.totalCount + additionalProperties = transactionListPageResponse.additionalProperties.toMutableMap() + } + + /** List of transactions matching the criteria */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [Transaction] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: Transaction) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of transactions matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [TransactionListPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .data() + * .hasMore() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TransactionListPageResponse = + TransactionListPageResponse( + checkRequired("data", data).map { it.toImmutable() }, + checkRequired("hasMore", hasMore), + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): TransactionListPageResponse = apply { + if (validated) { + return@apply + } + + data().forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionListPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "TransactionListPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListParams.kt new file mode 100644 index 00000000..5db7657f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionListParams.kt @@ -0,0 +1,489 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.util.Objects + +/** + * Retrieve a paginated list of transactions with optional filtering. The transactions can be + * filtered by customer ID, platform customer ID, UMA address, date range, status, and transaction + * type. + */ +class TransactionListParams +private constructor( + private val cursor: String?, + private val customerId: String?, + private val endDate: OffsetDateTime?, + private val limit: Long?, + private val platformCustomerId: String?, + private val receiverAccountIdentifier: String?, + private val reference: String?, + private val senderAccountIdentifier: String?, + private val sortOrder: SortOrder?, + private val startDate: OffsetDateTime?, + private val status: TransactionStatus?, + private val type: TransactionType?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** Filter by system customer ID */ + fun customerId(): String? = customerId + + /** Filter by end date (inclusive) in ISO 8601 format */ + fun endDate(): OffsetDateTime? = endDate + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Filter by platform-specific customer ID */ + fun platformCustomerId(): String? = platformCustomerId + + /** Filter by receiver account identifier */ + fun receiverAccountIdentifier(): String? = receiverAccountIdentifier + + /** Filter by reference */ + fun reference(): String? = reference + + /** Filter by sender account identifier */ + fun senderAccountIdentifier(): String? = senderAccountIdentifier + + /** Order to sort results in */ + fun sortOrder(): SortOrder? = sortOrder + + /** Filter by start date (inclusive) in ISO 8601 format */ + fun startDate(): OffsetDateTime? = startDate + + /** Filter by transaction status */ + fun status(): TransactionStatus? = status + + /** Filter by transaction type */ + fun type(): TransactionType? = type + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TransactionListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [TransactionListParams]. */ + fun builder() = Builder() + } + + /** A builder for [TransactionListParams]. */ + class Builder internal constructor() { + + private var cursor: String? = null + private var customerId: String? = null + private var endDate: OffsetDateTime? = null + private var limit: Long? = null + private var platformCustomerId: String? = null + private var receiverAccountIdentifier: String? = null + private var reference: String? = null + private var senderAccountIdentifier: String? = null + private var sortOrder: SortOrder? = null + private var startDate: OffsetDateTime? = null + private var status: TransactionStatus? = null + private var type: TransactionType? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(transactionListParams: TransactionListParams) = apply { + cursor = transactionListParams.cursor + customerId = transactionListParams.customerId + endDate = transactionListParams.endDate + limit = transactionListParams.limit + platformCustomerId = transactionListParams.platformCustomerId + receiverAccountIdentifier = transactionListParams.receiverAccountIdentifier + reference = transactionListParams.reference + senderAccountIdentifier = transactionListParams.senderAccountIdentifier + sortOrder = transactionListParams.sortOrder + startDate = transactionListParams.startDate + status = transactionListParams.status + type = transactionListParams.type + additionalHeaders = transactionListParams.additionalHeaders.toBuilder() + additionalQueryParams = transactionListParams.additionalQueryParams.toBuilder() + } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** Filter by system customer ID */ + fun customerId(customerId: String?) = apply { this.customerId = customerId } + + /** Filter by end date (inclusive) in ISO 8601 format */ + fun endDate(endDate: OffsetDateTime?) = apply { this.endDate = endDate } + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Filter by platform-specific customer ID */ + fun platformCustomerId(platformCustomerId: String?) = apply { + this.platformCustomerId = platformCustomerId + } + + /** Filter by receiver account identifier */ + fun receiverAccountIdentifier(receiverAccountIdentifier: String?) = apply { + this.receiverAccountIdentifier = receiverAccountIdentifier + } + + /** Filter by reference */ + fun reference(reference: String?) = apply { this.reference = reference } + + /** Filter by sender account identifier */ + fun senderAccountIdentifier(senderAccountIdentifier: String?) = apply { + this.senderAccountIdentifier = senderAccountIdentifier + } + + /** Order to sort results in */ + fun sortOrder(sortOrder: SortOrder?) = apply { this.sortOrder = sortOrder } + + /** Filter by start date (inclusive) in ISO 8601 format */ + fun startDate(startDate: OffsetDateTime?) = apply { this.startDate = startDate } + + /** Filter by transaction status */ + fun status(status: TransactionStatus?) = apply { this.status = status } + + /** Filter by transaction type */ + fun type(type: TransactionType?) = apply { this.type = type } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TransactionListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TransactionListParams = + TransactionListParams( + cursor, + customerId, + endDate, + limit, + platformCustomerId, + receiverAccountIdentifier, + reference, + senderAccountIdentifier, + sortOrder, + startDate, + status, + type, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + cursor?.let { put("cursor", it) } + customerId?.let { put("customerId", it) } + endDate?.let { put("endDate", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) } + limit?.let { put("limit", it.toString()) } + platformCustomerId?.let { put("platformCustomerId", it) } + receiverAccountIdentifier?.let { put("receiverAccountIdentifier", it) } + reference?.let { put("reference", it) } + senderAccountIdentifier?.let { put("senderAccountIdentifier", it) } + sortOrder?.let { put("sortOrder", it.toString()) } + startDate?.let { + put("startDate", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) + } + status?.let { put("status", it.toString()) } + type?.let { put("type", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + /** Order to sort results in */ + class SortOrder @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val ASC = of("asc") + + val DESC = of("desc") + + fun of(value: String) = SortOrder(JsonField.of(value)) + } + + /** An enum containing [SortOrder]'s known values. */ + enum class Known { + ASC, + DESC, + } + + /** + * An enum containing [SortOrder]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SortOrder] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ASC, + DESC, + /** + * An enum member indicating that [SortOrder] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ASC -> Value.ASC + DESC -> Value.DESC + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + ASC -> Known.ASC + DESC -> Known.DESC + else -> throw GridInvalidDataException("Unknown SortOrder: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): SortOrder = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SortOrder && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionListParams && + cursor == other.cursor && + customerId == other.customerId && + endDate == other.endDate && + limit == other.limit && + platformCustomerId == other.platformCustomerId && + receiverAccountIdentifier == other.receiverAccountIdentifier && + reference == other.reference && + senderAccountIdentifier == other.senderAccountIdentifier && + sortOrder == other.sortOrder && + startDate == other.startDate && + status == other.status && + type == other.type && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + cursor, + customerId, + endDate, + limit, + platformCustomerId, + receiverAccountIdentifier, + reference, + senderAccountIdentifier, + sortOrder, + startDate, + status, + type, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "TransactionListParams{cursor=$cursor, customerId=$customerId, endDate=$endDate, limit=$limit, platformCustomerId=$platformCustomerId, receiverAccountIdentifier=$receiverAccountIdentifier, reference=$reference, senderAccountIdentifier=$senderAccountIdentifier, sortOrder=$sortOrder, startDate=$startDate, status=$status, type=$type, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRejectParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRejectParams.kt new file mode 100644 index 00000000..a42c1cd0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRejectParams.kt @@ -0,0 +1,411 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Reject a pending incoming payment that was previously acknowledged with a 202 response. This + * endpoint allows platforms to asynchronously reject payments after additional processing. + */ +class TransactionRejectParams +private constructor( + private val transactionId: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun transactionId(): String? = transactionId + + /** + * Optional reason for rejecting the payment. This is just for debugging purposes or can be used + * for a platform's own purposes. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun reason(): String? = body.reason() + + /** + * Returns the raw JSON value of [reason]. + * + * Unlike [reason], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _reason(): JsonField = body._reason() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TransactionRejectParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [TransactionRejectParams]. */ + fun builder() = Builder() + } + + /** A builder for [TransactionRejectParams]. */ + class Builder internal constructor() { + + private var transactionId: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(transactionRejectParams: TransactionRejectParams) = apply { + transactionId = transactionRejectParams.transactionId + body = transactionRejectParams.body.toBuilder() + additionalHeaders = transactionRejectParams.additionalHeaders.toBuilder() + additionalQueryParams = transactionRejectParams.additionalQueryParams.toBuilder() + } + + fun transactionId(transactionId: String?) = apply { this.transactionId = transactionId } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [reason] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * Optional reason for rejecting the payment. This is just for debugging purposes or can be + * used for a platform's own purposes. + */ + fun reason(reason: String) = apply { body.reason(reason) } + + /** + * Sets [Builder.reason] to an arbitrary JSON value. + * + * You should usually call [Builder.reason] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun reason(reason: JsonField) = apply { body.reason(reason) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TransactionRejectParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TransactionRejectParams = + TransactionRejectParams( + transactionId, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> transactionId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val reason: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("reason") @ExcludeMissing reason: JsonField = JsonMissing.of() + ) : this(reason, mutableMapOf()) + + /** + * Optional reason for rejecting the payment. This is just for debugging purposes or can be + * used for a platform's own purposes. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun reason(): String? = reason.getNullable("reason") + + /** + * Returns the raw JSON value of [reason]. + * + * Unlike [reason], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reason") @ExcludeMissing fun _reason(): JsonField = reason + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Body]. */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var reason: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + reason = body.reason + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * Optional reason for rejecting the payment. This is just for debugging purposes or can + * be used for a platform's own purposes. + */ + fun reason(reason: String) = reason(JsonField.of(reason)) + + /** + * Sets [Builder.reason] to an arbitrary JSON value. + * + * You should usually call [Builder.reason] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun reason(reason: JsonField) = apply { this.reason = reason } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Body = Body(reason, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + reason() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (reason.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + reason == other.reason && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(reason, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{reason=$reason, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionRejectParams && + transactionId == other.transactionId && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(transactionId, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TransactionRejectParams{transactionId=$transactionId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRetrieveParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRetrieveParams.kt new file mode 100644 index 00000000..6b4c3961 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionRetrieveParams.kt @@ -0,0 +1,190 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import java.util.Objects + +/** Retrieve detailed information about a specific transaction */ +class TransactionRetrieveParams +private constructor( + private val transactionId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun transactionId(): String? = transactionId + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): TransactionRetrieveParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of [TransactionRetrieveParams]. + */ + fun builder() = Builder() + } + + /** A builder for [TransactionRetrieveParams]. */ + class Builder internal constructor() { + + private var transactionId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(transactionRetrieveParams: TransactionRetrieveParams) = apply { + transactionId = transactionRetrieveParams.transactionId + additionalHeaders = transactionRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = transactionRetrieveParams.additionalQueryParams.toBuilder() + } + + fun transactionId(transactionId: String?) = apply { this.transactionId = transactionId } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TransactionRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): TransactionRetrieveParams = + TransactionRetrieveParams( + transactionId, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> transactionId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionRetrieveParams && + transactionId == other.transactionId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(transactionId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TransactionRetrieveParams{transactionId=$transactionId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionSourceOneOf.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionSourceOneOf.kt new file mode 100644 index 00000000..6d1cad6f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionSourceOneOf.kt @@ -0,0 +1,948 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** Source account details */ +@JsonDeserialize(using = TransactionSourceOneOf.Deserializer::class) +@JsonSerialize(using = TransactionSourceOneOf.Serializer::class) +class TransactionSourceOneOf +private constructor( + private val account: Account? = null, + private val umaAddress: UmaAddress? = null, + private val _json: JsonValue? = null, +) { + + /** Source account details */ + fun account(): Account? = account + + /** UMA address source details */ + fun umaAddress(): UmaAddress? = umaAddress + + fun isAccount(): Boolean = account != null + + fun isUmaAddress(): Boolean = umaAddress != null + + /** Source account details */ + fun asAccount(): Account = account.getOrThrow("account") + + /** UMA address source details */ + fun asUmaAddress(): UmaAddress = umaAddress.getOrThrow("umaAddress") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + account != null -> visitor.visitAccount(account) + umaAddress != null -> visitor.visitUmaAddress(umaAddress) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): TransactionSourceOneOf = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAccount(account: Account) { + account.validate() + } + + override fun visitUmaAddress(umaAddress: UmaAddress) { + umaAddress.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAccount(account: Account) = account.validity() + + override fun visitUmaAddress(umaAddress: UmaAddress) = umaAddress.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionSourceOneOf && + account == other.account && + umaAddress == other.umaAddress + } + + override fun hashCode(): Int = Objects.hash(account, umaAddress) + + override fun toString(): String = + when { + account != null -> "TransactionSourceOneOf{account=$account}" + umaAddress != null -> "TransactionSourceOneOf{umaAddress=$umaAddress}" + _json != null -> "TransactionSourceOneOf{_unknown=$_json}" + else -> throw IllegalStateException("Invalid TransactionSourceOneOf") + } + + companion object { + + /** Source account details */ + fun ofAccount(account: Account) = TransactionSourceOneOf(account = account) + + /** UMA address source details */ + fun ofUmaAddress(umaAddress: UmaAddress) = TransactionSourceOneOf(umaAddress = umaAddress) + } + + /** + * An interface that defines how to map each variant of [TransactionSourceOneOf] to a value of + * type [T]. + */ + interface Visitor { + + /** Source account details */ + fun visitAccount(account: Account): T + + /** UMA address source details */ + fun visitUmaAddress(umaAddress: UmaAddress): T + + /** + * Maps an unknown variant of [TransactionSourceOneOf] to a value of type [T]. + * + * An instance of [TransactionSourceOneOf] can contain an unknown variant if it was + * deserialized from data that doesn't match any known variant. For example, if the SDK is + * on an older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown TransactionSourceOneOf: $json") + } + } + + internal class Deserializer : + BaseDeserializer(TransactionSourceOneOf::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): TransactionSourceOneOf { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + TransactionSourceOneOf(account = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + TransactionSourceOneOf(umaAddress = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with all + // the possible variants (e.g. deserializing from boolean). + 0 -> TransactionSourceOneOf(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : + BaseSerializer(TransactionSourceOneOf::class) { + + override fun serialize( + value: TransactionSourceOneOf, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.account != null -> generator.writeObject(value.account) + value.umaAddress != null -> generator.writeObject(value.umaAddress) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid TransactionSourceOneOf") + } + } + } + + /** Source account details */ + class Account + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currency: JsonField, + private val accountId: JsonField, + private val sourceType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of(), + @JsonProperty("sourceType") + @ExcludeMissing + sourceType: JsonField = JsonMissing.of(), + ) : this(currency, accountId, sourceType, mutableMapOf()) + + fun toBaseTransactionSource(): BaseTransactionSource = + BaseTransactionSource.builder().currency(currency).build() + + /** + * Currency code for the source + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * Source account identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun sourceType(): SourceType = sourceType.getRequired("sourceType") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + /** + * Returns the raw JSON value of [sourceType]. + * + * Unlike [sourceType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sourceType") + @ExcludeMissing + fun _sourceType(): JsonField = sourceType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Account]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .sourceType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Account]. */ + class Builder internal constructor() { + + private var currency: JsonField = JsonMissing.of() + private var accountId: JsonField? = null + private var sourceType: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(account: Account) = apply { + currency = account.currency + accountId = account.accountId + sourceType = account.sourceType + additionalProperties = account.additionalProperties.toMutableMap() + } + + /** Currency code for the source */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + /** Source account identifier */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun sourceType(sourceType: SourceType) = sourceType(JsonField.of(sourceType)) + + /** + * Sets [Builder.sourceType] to an arbitrary JSON value. + * + * You should usually call [Builder.sourceType] with a well-typed [SourceType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sourceType(sourceType: JsonField) = apply { + this.sourceType = sourceType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Account]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .sourceType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Account = + Account( + currency, + checkRequired("accountId", accountId), + checkRequired("sourceType", sourceType), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Account = apply { + if (validated) { + return@apply + } + + currency() + accountId() + sourceType().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currency.asKnown() == null) 0 else 1) + + (if (accountId.asKnown() == null) 0 else 1) + + (sourceType.asKnown()?.validity() ?: 0) + + class SourceType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val ACCOUNT = of("ACCOUNT") + + fun of(value: String) = SourceType(JsonField.of(value)) + } + + /** An enum containing [SourceType]'s known values. */ + enum class Known { + ACCOUNT + } + + /** + * An enum containing [SourceType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SourceType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ACCOUNT, + /** + * An enum member indicating that [SourceType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ACCOUNT -> Value.ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACCOUNT -> Known.ACCOUNT + else -> throw GridInvalidDataException("Unknown SourceType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): SourceType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SourceType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Account && + currency == other.currency && + accountId == other.accountId && + sourceType == other.sourceType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currency, accountId, sourceType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Account{currency=$currency, accountId=$accountId, sourceType=$sourceType, additionalProperties=$additionalProperties}" + } + + /** UMA address source details */ + class UmaAddress + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currency: JsonField, + private val sourceType: JsonField, + private val umaAddress: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("sourceType") + @ExcludeMissing + sourceType: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + ) : this(currency, sourceType, umaAddress, mutableMapOf()) + + fun toBaseTransactionSource(): BaseTransactionSource = + BaseTransactionSource.builder().currency(currency).build() + + /** + * Currency code for the source + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun sourceType(): SourceType = sourceType.getRequired("sourceType") + + /** + * UMA address of the sender + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [sourceType]. + * + * Unlike [sourceType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sourceType") + @ExcludeMissing + fun _sourceType(): JsonField = sourceType + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaAddress]. + * + * The following fields are required: + * ```kotlin + * .sourceType() + * .umaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaAddress]. */ + class Builder internal constructor() { + + private var currency: JsonField = JsonMissing.of() + private var sourceType: JsonField? = null + private var umaAddress: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(umaAddress: UmaAddress) = apply { + currency = umaAddress.currency + sourceType = umaAddress.sourceType + this.umaAddress = umaAddress.umaAddress + additionalProperties = umaAddress.additionalProperties.toMutableMap() + } + + /** Currency code for the source */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun sourceType(sourceType: SourceType) = sourceType(JsonField.of(sourceType)) + + /** + * Sets [Builder.sourceType] to an arbitrary JSON value. + * + * You should usually call [Builder.sourceType] with a well-typed [SourceType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sourceType(sourceType: JsonField) = apply { + this.sourceType = sourceType + } + + /** UMA address of the sender */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { this.umaAddress = umaAddress } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UmaAddress]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .sourceType() + * .umaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaAddress = + UmaAddress( + currency, + checkRequired("sourceType", sourceType), + checkRequired("umaAddress", umaAddress), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UmaAddress = apply { + if (validated) { + return@apply + } + + currency() + sourceType().validate() + umaAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currency.asKnown() == null) 0 else 1) + + (sourceType.asKnown()?.validity() ?: 0) + + (if (umaAddress.asKnown() == null) 0 else 1) + + class SourceType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val UMA_ADDRESS = of("UMA_ADDRESS") + + fun of(value: String) = SourceType(JsonField.of(value)) + } + + /** An enum containing [SourceType]'s known values. */ + enum class Known { + UMA_ADDRESS + } + + /** + * An enum containing [SourceType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SourceType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + UMA_ADDRESS, + /** + * An enum member indicating that [SourceType] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + UMA_ADDRESS -> Value.UMA_ADDRESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + UMA_ADDRESS -> Known.UMA_ADDRESS + else -> throw GridInvalidDataException("Unknown SourceType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): SourceType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SourceType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaAddress && + currency == other.currency && + sourceType == other.sourceType && + umaAddress == other.umaAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currency, sourceType, umaAddress, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UmaAddress{currency=$currency, sourceType=$sourceType, umaAddress=$umaAddress, additionalProperties=$additionalProperties}" + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionStatus.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionStatus.kt new file mode 100644 index 00000000..f2c4c821 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionStatus.kt @@ -0,0 +1,171 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.errors.GridInvalidDataException + +/** Status of a payment transaction */ +class TransactionStatus @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val CREATED = of("CREATED") + + val PENDING = of("PENDING") + + val PROCESSING = of("PROCESSING") + + val COMPLETED = of("COMPLETED") + + val REJECTED = of("REJECTED") + + val FAILED = of("FAILED") + + val REFUNDED = of("REFUNDED") + + val EXPIRED = of("EXPIRED") + + fun of(value: String) = TransactionStatus(JsonField.of(value)) + } + + /** An enum containing [TransactionStatus]'s known values. */ + enum class Known { + CREATED, + PENDING, + PROCESSING, + COMPLETED, + REJECTED, + FAILED, + REFUNDED, + EXPIRED, + } + + /** + * An enum containing [TransactionStatus]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [TransactionStatus] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + CREATED, + PENDING, + PROCESSING, + COMPLETED, + REJECTED, + FAILED, + REFUNDED, + EXPIRED, + /** + * An enum member indicating that [TransactionStatus] was instantiated with an unknown + * value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + CREATED -> Value.CREATED + PENDING -> Value.PENDING + PROCESSING -> Value.PROCESSING + COMPLETED -> Value.COMPLETED + REJECTED -> Value.REJECTED + FAILED -> Value.FAILED + REFUNDED -> Value.REFUNDED + EXPIRED -> Value.EXPIRED + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + CREATED -> Known.CREATED + PENDING -> Known.PENDING + PROCESSING -> Known.PROCESSING + COMPLETED -> Known.COMPLETED + REJECTED -> Known.REJECTED + FAILED -> Known.FAILED + REFUNDED -> Known.REFUNDED + EXPIRED -> Known.EXPIRED + else -> throw GridInvalidDataException("Unknown TransactionStatus: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): TransactionStatus = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionStatus && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionType.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionType.kt new file mode 100644 index 00000000..ec820aa0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transactions/TransactionType.kt @@ -0,0 +1,134 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.errors.GridInvalidDataException + +/** Type of transaction (incoming payment or outgoing payment) */ +class TransactionType @JsonCreator private constructor(private val value: JsonField) : + Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't match + * any known member, and you want to know that value. For example, if the SDK is on an older + * version than the API, then the API may respond with new members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val INCOMING = of("INCOMING") + + val OUTGOING = of("OUTGOING") + + fun of(value: String) = TransactionType(JsonField.of(value)) + } + + /** An enum containing [TransactionType]'s known values. */ + enum class Known { + INCOMING, + OUTGOING, + } + + /** + * An enum containing [TransactionType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [TransactionType] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the SDK + * is on an older version than the API, then the API may respond with new members that the SDK + * is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + INCOMING, + OUTGOING, + /** + * An enum member indicating that [TransactionType] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] if + * the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want to + * throw for the unknown case. + */ + fun value(): Value = + when (this) { + INCOMING -> Value.INCOMING + OUTGOING -> Value.OUTGOING + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't want + * to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + INCOMING -> Known.INCOMING + OUTGOING -> Known.OUTGOING + else -> throw GridInvalidDataException("Unknown TransactionType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging and + * generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the expected + * primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): TransactionType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransactionType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/BaseTransactionDestination.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/BaseTransactionDestination.kt new file mode 100644 index 00000000..ab28176c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/BaseTransactionDestination.kt @@ -0,0 +1,156 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferin + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class BaseTransactionDestination +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val currency: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("currency") @ExcludeMissing currency: JsonField = JsonMissing.of() + ) : this(currency, mutableMapOf()) + + /** + * Currency code for the destination + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BaseTransactionDestination]. + */ + fun builder() = Builder() + } + + /** A builder for [BaseTransactionDestination]. */ + class Builder internal constructor() { + + private var currency: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(baseTransactionDestination: BaseTransactionDestination) = apply { + currency = baseTransactionDestination.currency + additionalProperties = baseTransactionDestination.additionalProperties.toMutableMap() + } + + /** Currency code for the destination */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [BaseTransactionDestination]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BaseTransactionDestination = + BaseTransactionDestination(currency, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): BaseTransactionDestination = apply { + if (validated) { + return@apply + } + + currency() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (currency.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BaseTransactionDestination && + currency == other.currency && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(currency, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BaseTransactionDestination{currency=$currency, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/Transaction.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/Transaction.kt new file mode 100644 index 00000000..df1de149 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/Transaction.kt @@ -0,0 +1,1718 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferin + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.BaseDeserializer +import com.grid.api.core.BaseSerializer +import com.grid.api.core.Enum +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.allMaxBy +import com.grid.api.core.checkRequired +import com.grid.api.core.getOrThrow +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.transactions.TransactionStatus +import com.grid.api.models.transactions.TransactionType +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects + +class Transaction +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val customerId: JsonField, + private val destination: JsonField, + private val platformCustomerId: JsonField, + private val status: JsonField, + private val type: JsonField, + private val counterpartyInformation: JsonField, + private val createdAt: JsonField, + private val description: JsonField, + private val settledAt: JsonField, + private val updatedAt: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("customerId") + @ExcludeMissing + customerId: JsonField = JsonMissing.of(), + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("platformCustomerId") + @ExcludeMissing + platformCustomerId: JsonField = JsonMissing.of(), + @JsonProperty("status") + @ExcludeMissing + status: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("counterpartyInformation") + @ExcludeMissing + counterpartyInformation: JsonField = JsonMissing.of(), + @JsonProperty("createdAt") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("description") + @ExcludeMissing + description: JsonField = JsonMissing.of(), + @JsonProperty("settledAt") + @ExcludeMissing + settledAt: JsonField = JsonMissing.of(), + @JsonProperty("updatedAt") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + ) : this( + id, + customerId, + destination, + platformCustomerId, + status, + type, + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + mutableMapOf(), + ) + + /** + * Unique identifier for the transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * System ID of the customer (sender for outgoing, recipient for incoming) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun customerId(): String = customerId.getRequired("customerId") + + /** + * Destination account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Destination = destination.getRequired("destination") + + /** + * Platform-specific ID of the customer (sender for outgoing, recipient for incoming) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun platformCustomerId(): String = platformCustomerId.getRequired("platformCustomerId") + + /** + * Status of a payment transaction + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun status(): TransactionStatus = status.getRequired("status") + + /** + * Type of transaction (incoming payment or outgoing payment) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun type(): TransactionType = type.getRequired("type") + + /** + * Additional information about the counterparty, if available and relevant to the transaction + * and platform. Only applicable for transactions to/from UMA addresses. + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun counterpartyInformation(): CounterpartyInformation? = + counterpartyInformation.getNullable("counterpartyInformation") + + /** + * When the transaction was created + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun createdAt(): OffsetDateTime? = createdAt.getNullable("createdAt") + + /** + * Optional memo or description for the payment + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun description(): String? = description.getNullable("description") + + /** + * When the payment was or will be settled + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun settledAt(): OffsetDateTime? = settledAt.getNullable("settledAt") + + /** + * When the transaction was last updated + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun updatedAt(): OffsetDateTime? = updatedAt.getNullable("updatedAt") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [customerId]. + * + * Unlike [customerId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("customerId") @ExcludeMissing fun _customerId(): JsonField = customerId + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [platformCustomerId]. + * + * Unlike [platformCustomerId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("platformCustomerId") + @ExcludeMissing + fun _platformCustomerId(): JsonField = platformCustomerId + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + /** + * Returns the raw JSON value of [counterpartyInformation]. + * + * Unlike [counterpartyInformation], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("counterpartyInformation") + @ExcludeMissing + fun _counterpartyInformation(): JsonField = counterpartyInformation + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("createdAt") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [settledAt]. + * + * Unlike [settledAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("settledAt") + @ExcludeMissing + fun _settledAt(): JsonField = settledAt + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updatedAt") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Transaction]. + * + * The following fields are required: + * ```kotlin + * .id() + * .customerId() + * .destination() + * .platformCustomerId() + * .status() + * .type() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Transaction]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var customerId: JsonField? = null + private var destination: JsonField? = null + private var platformCustomerId: JsonField? = null + private var status: JsonField? = null + private var type: JsonField? = null + private var counterpartyInformation: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var description: JsonField = JsonMissing.of() + private var settledAt: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(transaction: Transaction) = apply { + id = transaction.id + customerId = transaction.customerId + destination = transaction.destination + platformCustomerId = transaction.platformCustomerId + status = transaction.status + type = transaction.type + counterpartyInformation = transaction.counterpartyInformation + createdAt = transaction.createdAt + description = transaction.description + settledAt = transaction.settledAt + updatedAt = transaction.updatedAt + additionalProperties = transaction.additionalProperties.toMutableMap() + } + + /** Unique identifier for the transaction */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** System ID of the customer (sender for outgoing, recipient for incoming) */ + fun customerId(customerId: String) = customerId(JsonField.of(customerId)) + + /** + * Sets [Builder.customerId] to an arbitrary JSON value. + * + * You should usually call [Builder.customerId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun customerId(customerId: JsonField) = apply { this.customerId = customerId } + + /** Destination account details */ + fun destination(destination: Destination) = destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Destination] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Alias for calling [destination] with `Destination.ofAccount(account)`. */ + fun destination(account: Destination.Account) = destination(Destination.ofAccount(account)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * Destination.Account.builder() + * .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + * .accountId(accountId) + * .build() + * ``` + */ + fun accountDestination(accountId: String) = + destination( + Destination.Account.builder() + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .accountId(accountId) + .build() + ) + + /** Alias for calling [destination] with `Destination.ofUmaAddress(umaAddress)`. */ + fun destination(umaAddress: Destination.UmaAddress) = + destination(Destination.ofUmaAddress(umaAddress)) + + /** + * Alias for calling [destination] with the following: + * ```kotlin + * Destination.UmaAddress.builder() + * .destinationType(Transaction.Destination.UmaAddress.DestinationType.UMA_ADDRESS) + * .umaAddress(umaAddress) + * .build() + * ``` + */ + fun umaAddressDestination(umaAddress: String) = + destination( + Destination.UmaAddress.builder() + .destinationType(Transaction.Destination.UmaAddress.DestinationType.UMA_ADDRESS) + .umaAddress(umaAddress) + .build() + ) + + /** Platform-specific ID of the customer (sender for outgoing, recipient for incoming) */ + fun platformCustomerId(platformCustomerId: String) = + platformCustomerId(JsonField.of(platformCustomerId)) + + /** + * Sets [Builder.platformCustomerId] to an arbitrary JSON value. + * + * You should usually call [Builder.platformCustomerId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun platformCustomerId(platformCustomerId: JsonField) = apply { + this.platformCustomerId = platformCustomerId + } + + /** Status of a payment transaction */ + fun status(status: TransactionStatus) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [TransactionStatus] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** Type of transaction (incoming payment or outgoing payment) */ + fun type(type: TransactionType) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [TransactionType] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + /** + * Additional information about the counterparty, if available and relevant to the + * transaction and platform. Only applicable for transactions to/from UMA addresses. + */ + fun counterpartyInformation(counterpartyInformation: CounterpartyInformation) = + counterpartyInformation(JsonField.of(counterpartyInformation)) + + /** + * Sets [Builder.counterpartyInformation] to an arbitrary JSON value. + * + * You should usually call [Builder.counterpartyInformation] with a well-typed + * [CounterpartyInformation] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun counterpartyInformation(counterpartyInformation: JsonField) = + apply { + this.counterpartyInformation = counterpartyInformation + } + + /** When the transaction was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** Optional memo or description for the payment */ + fun description(description: String) = description(JsonField.of(description)) + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun description(description: JsonField) = apply { this.description = description } + + /** When the payment was or will be settled */ + fun settledAt(settledAt: OffsetDateTime) = settledAt(JsonField.of(settledAt)) + + /** + * Sets [Builder.settledAt] to an arbitrary JSON value. + * + * You should usually call [Builder.settledAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun settledAt(settledAt: JsonField) = apply { this.settledAt = settledAt } + + /** When the transaction was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Transaction]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .id() + * .customerId() + * .destination() + * .platformCustomerId() + * .status() + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Transaction = + Transaction( + checkRequired("id", id), + checkRequired("customerId", customerId), + checkRequired("destination", destination), + checkRequired("platformCustomerId", platformCustomerId), + checkRequired("status", status), + checkRequired("type", type), + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Transaction = apply { + if (validated) { + return@apply + } + + id() + customerId() + destination().validate() + platformCustomerId() + status().validate() + type().validate() + counterpartyInformation()?.validate() + createdAt() + description() + settledAt() + updatedAt() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (id.asKnown() == null) 0 else 1) + + (if (customerId.asKnown() == null) 0 else 1) + + (destination.asKnown()?.validity() ?: 0) + + (if (platformCustomerId.asKnown() == null) 0 else 1) + + (status.asKnown()?.validity() ?: 0) + + (type.asKnown()?.validity() ?: 0) + + (counterpartyInformation.asKnown()?.validity() ?: 0) + + (if (createdAt.asKnown() == null) 0 else 1) + + (if (description.asKnown() == null) 0 else 1) + + (if (settledAt.asKnown() == null) 0 else 1) + + (if (updatedAt.asKnown() == null) 0 else 1) + + /** Destination account details */ + @JsonDeserialize(using = Destination.Deserializer::class) + @JsonSerialize(using = Destination.Serializer::class) + class Destination + private constructor( + private val account: Account? = null, + private val umaAddress: UmaAddress? = null, + private val _json: JsonValue? = null, + ) { + + /** Destination account details */ + fun account(): Account? = account + + /** UMA address destination details */ + fun umaAddress(): UmaAddress? = umaAddress + + fun isAccount(): Boolean = account != null + + fun isUmaAddress(): Boolean = umaAddress != null + + /** Destination account details */ + fun asAccount(): Account = account.getOrThrow("account") + + /** UMA address destination details */ + fun asUmaAddress(): UmaAddress = umaAddress.getOrThrow("umaAddress") + + fun _json(): JsonValue? = _json + + fun accept(visitor: Visitor): T = + when { + account != null -> visitor.visitAccount(account) + umaAddress != null -> visitor.visitUmaAddress(umaAddress) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Destination = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitAccount(account: Account) { + account.validate() + } + + override fun visitUmaAddress(umaAddress: UmaAddress) { + umaAddress.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + accept( + object : Visitor { + override fun visitAccount(account: Account) = account.validity() + + override fun visitUmaAddress(umaAddress: UmaAddress) = umaAddress.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Destination && + account == other.account && + umaAddress == other.umaAddress + } + + override fun hashCode(): Int = Objects.hash(account, umaAddress) + + override fun toString(): String = + when { + account != null -> "Destination{account=$account}" + umaAddress != null -> "Destination{umaAddress=$umaAddress}" + _json != null -> "Destination{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Destination") + } + + companion object { + + /** Destination account details */ + fun ofAccount(account: Account) = Destination(account = account) + + /** UMA address destination details */ + fun ofUmaAddress(umaAddress: UmaAddress) = Destination(umaAddress = umaAddress) + } + + /** + * An interface that defines how to map each variant of [Destination] to a value of type + * [T]. + */ + interface Visitor { + + /** Destination account details */ + fun visitAccount(account: Account): T + + /** UMA address destination details */ + fun visitUmaAddress(umaAddress: UmaAddress): T + + /** + * Maps an unknown variant of [Destination] to a value of type [T]. + * + * An instance of [Destination] can contain an unknown variant if it was deserialized + * from data that doesn't match any known variant. For example, if the SDK is on an + * older version than the API, then the API may respond with new variants that the SDK + * is unaware of. + * + * @throws GridInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw GridInvalidDataException("Unknown Destination: $json") + } + } + + internal class Deserializer : BaseDeserializer(Destination::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Destination { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Destination(account = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Destination(umaAddress = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Destination(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Destination::class) { + + override fun serialize( + value: Destination, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.account != null -> generator.writeObject(value.account) + value.umaAddress != null -> generator.writeObject(value.umaAddress) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Destination") + } + } + } + + /** Destination account details */ + class Account + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currency: JsonField, + private val accountId: JsonField, + private val destinationType: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of(), + @JsonProperty("destinationType") + @ExcludeMissing + destinationType: JsonField = JsonMissing.of(), + ) : this(currency, accountId, destinationType, mutableMapOf()) + + fun toBaseTransactionDestination(): BaseTransactionDestination = + BaseTransactionDestination.builder().currency(currency).build() + + /** + * Currency code for the destination + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * Destination account identifier + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun destinationType(): DestinationType = destinationType.getRequired("destinationType") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("accountId") + @ExcludeMissing + fun _accountId(): JsonField = accountId + + /** + * Returns the raw JSON value of [destinationType]. + * + * Unlike [destinationType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("destinationType") + @ExcludeMissing + fun _destinationType(): JsonField = destinationType + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Account]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .destinationType() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Account]. */ + class Builder internal constructor() { + + private var currency: JsonField = JsonMissing.of() + private var accountId: JsonField? = null + private var destinationType: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(account: Account) = apply { + currency = account.currency + accountId = account.accountId + destinationType = account.destinationType + additionalProperties = account.additionalProperties.toMutableMap() + } + + /** Currency code for the destination */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + /** Destination account identifier */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun destinationType(destinationType: DestinationType) = + destinationType(JsonField.of(destinationType)) + + /** + * Sets [Builder.destinationType] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationType] with a well-typed + * [DestinationType] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun destinationType(destinationType: JsonField) = apply { + this.destinationType = destinationType + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Account]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * .destinationType() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Account = + Account( + currency, + checkRequired("accountId", accountId), + checkRequired("destinationType", destinationType), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Account = apply { + if (validated) { + return@apply + } + + currency() + accountId() + destinationType().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currency.asKnown() == null) 0 else 1) + + (if (accountId.asKnown() == null) 0 else 1) + + (destinationType.asKnown()?.validity() ?: 0) + + class DestinationType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val ACCOUNT = of("ACCOUNT") + + fun of(value: String) = DestinationType(JsonField.of(value)) + } + + /** An enum containing [DestinationType]'s known values. */ + enum class Known { + ACCOUNT + } + + /** + * An enum containing [DestinationType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [DestinationType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ACCOUNT, + /** + * An enum member indicating that [DestinationType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ACCOUNT -> Value.ACCOUNT + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACCOUNT -> Known.ACCOUNT + else -> throw GridInvalidDataException("Unknown DestinationType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): DestinationType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DestinationType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Account && + currency == other.currency && + accountId == other.accountId && + destinationType == other.destinationType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currency, accountId, destinationType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Account{currency=$currency, accountId=$accountId, destinationType=$destinationType, additionalProperties=$additionalProperties}" + } + + /** UMA address destination details */ + class UmaAddress + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val currency: JsonField, + private val destinationType: JsonField, + private val umaAddress: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("currency") + @ExcludeMissing + currency: JsonField = JsonMissing.of(), + @JsonProperty("destinationType") + @ExcludeMissing + destinationType: JsonField = JsonMissing.of(), + @JsonProperty("umaAddress") + @ExcludeMissing + umaAddress: JsonField = JsonMissing.of(), + ) : this(currency, destinationType, umaAddress, mutableMapOf()) + + fun toBaseTransactionDestination(): BaseTransactionDestination = + BaseTransactionDestination.builder().currency(currency).build() + + /** + * Currency code for the destination + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun currency(): String? = currency.getNullable("currency") + + /** + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun destinationType(): DestinationType = destinationType.getRequired("destinationType") + + /** + * UMA address of the recipient + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected + * value). + */ + fun umaAddress(): String = umaAddress.getRequired("umaAddress") + + /** + * Returns the raw JSON value of [currency]. + * + * Unlike [currency], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("currency") @ExcludeMissing fun _currency(): JsonField = currency + + /** + * Returns the raw JSON value of [destinationType]. + * + * Unlike [destinationType], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("destinationType") + @ExcludeMissing + fun _destinationType(): JsonField = destinationType + + /** + * Returns the raw JSON value of [umaAddress]. + * + * Unlike [umaAddress], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("umaAddress") + @ExcludeMissing + fun _umaAddress(): JsonField = umaAddress + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaAddress]. + * + * The following fields are required: + * ```kotlin + * .destinationType() + * .umaAddress() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaAddress]. */ + class Builder internal constructor() { + + private var currency: JsonField = JsonMissing.of() + private var destinationType: JsonField? = null + private var umaAddress: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(umaAddress: UmaAddress) = apply { + currency = umaAddress.currency + destinationType = umaAddress.destinationType + this.umaAddress = umaAddress.umaAddress + additionalProperties = umaAddress.additionalProperties.toMutableMap() + } + + /** Currency code for the destination */ + fun currency(currency: String) = currency(JsonField.of(currency)) + + /** + * Sets [Builder.currency] to an arbitrary JSON value. + * + * You should usually call [Builder.currency] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun currency(currency: JsonField) = apply { this.currency = currency } + + fun destinationType(destinationType: DestinationType) = + destinationType(JsonField.of(destinationType)) + + /** + * Sets [Builder.destinationType] to an arbitrary JSON value. + * + * You should usually call [Builder.destinationType] with a well-typed + * [DestinationType] value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun destinationType(destinationType: JsonField) = apply { + this.destinationType = destinationType + } + + /** UMA address of the recipient */ + fun umaAddress(umaAddress: String) = umaAddress(JsonField.of(umaAddress)) + + /** + * Sets [Builder.umaAddress] to an arbitrary JSON value. + * + * You should usually call [Builder.umaAddress] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun umaAddress(umaAddress: JsonField) = apply { + this.umaAddress = umaAddress + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = + apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { + additionalProperties.remove(key) + } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UmaAddress]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destinationType() + * .umaAddress() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaAddress = + UmaAddress( + currency, + checkRequired("destinationType", destinationType), + checkRequired("umaAddress", umaAddress), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UmaAddress = apply { + if (validated) { + return@apply + } + + currency() + destinationType().validate() + umaAddress() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (currency.asKnown() == null) 0 else 1) + + (destinationType.asKnown()?.validity() ?: 0) + + (if (umaAddress.asKnown() == null) 0 else 1) + + class DestinationType + @JsonCreator + private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that + * doesn't match any known member, and you want to know that value. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val UMA_ADDRESS = of("UMA_ADDRESS") + + fun of(value: String) = DestinationType(JsonField.of(value)) + } + + /** An enum containing [DestinationType]'s known values. */ + enum class Known { + UMA_ADDRESS + } + + /** + * An enum containing [DestinationType]'s known values, as well as an [_UNKNOWN] + * member. + * + * An instance of [DestinationType] can contain an unknown value in a couple of + * cases: + * - It was deserialized from data that doesn't match any known member. For example, + * if the SDK is on an older version than the API, then the API may respond with + * new members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + UMA_ADDRESS, + /** + * An enum member indicating that [DestinationType] was instantiated with an + * unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if + * you want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + UMA_ADDRESS -> Value.UMA_ADDRESS + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + UMA_ADDRESS -> Known.UMA_ADDRESS + else -> throw GridInvalidDataException("Unknown DestinationType: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): DestinationType = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DestinationType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaAddress && + currency == other.currency && + destinationType == other.destinationType && + umaAddress == other.umaAddress && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(currency, destinationType, umaAddress, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UmaAddress{currency=$currency, destinationType=$destinationType, umaAddress=$umaAddress, additionalProperties=$additionalProperties}" + } + } + + /** + * Additional information about the counterparty, if available and relevant to the transaction + * and platform. Only applicable for transactions to/from UMA addresses. + */ + class CounterpartyInformation + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [CounterpartyInformation]. + */ + fun builder() = Builder() + } + + /** A builder for [CounterpartyInformation]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(counterpartyInformation: CounterpartyInformation) = apply { + additionalProperties = counterpartyInformation.additionalProperties.toMutableMap() + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [CounterpartyInformation]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CounterpartyInformation = + CounterpartyInformation(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): CounterpartyInformation = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CounterpartyInformation && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CounterpartyInformation{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Transaction && + id == other.id && + customerId == other.customerId && + destination == other.destination && + platformCustomerId == other.platformCustomerId && + status == other.status && + type == other.type && + counterpartyInformation == other.counterpartyInformation && + createdAt == other.createdAt && + description == other.description && + settledAt == other.settledAt && + updatedAt == other.updatedAt && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + customerId, + destination, + platformCustomerId, + status, + type, + counterpartyInformation, + createdAt, + description, + settledAt, + updatedAt, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Transaction{id=$id, customerId=$customerId, destination=$destination, platformCustomerId=$platformCustomerId, status=$status, type=$type, counterpartyInformation=$counterpartyInformation, createdAt=$createdAt, description=$description, settledAt=$settledAt, updatedAt=$updatedAt, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/TransferInCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/TransferInCreateParams.kt new file mode 100644 index 00000000..23685603 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferin/TransferInCreateParams.kt @@ -0,0 +1,900 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferin + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** + * Transfer funds from an external account to an internal account for a specific customer. This + * endpoint should only be used for external account sources with pull functionality (e.g. ACH + * Pull). Otherwise, use the paymentInstructions on the internal account to deposit funds. + */ +class TransferInCreateParams +private constructor( + private val idempotencyKey: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun idempotencyKey(): String? = idempotencyKey + + /** + * Destination internal account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Destination = body.destination() + + /** + * Source external account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): Source = body.source() + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun amount(): Long? = body.amount() + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _destination(): JsonField = body._destination() + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _source(): JsonField = body._source() + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _amount(): JsonField = body._amount() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TransferInCreateParams]. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TransferInCreateParams]. */ + class Builder internal constructor() { + + private var idempotencyKey: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(transferInCreateParams: TransferInCreateParams) = apply { + idempotencyKey = transferInCreateParams.idempotencyKey + body = transferInCreateParams.body.toBuilder() + additionalHeaders = transferInCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = transferInCreateParams.additionalQueryParams.toBuilder() + } + + fun idempotencyKey(idempotencyKey: String?) = apply { this.idempotencyKey = idempotencyKey } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [destination] + * - [source] + * - [amount] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Destination internal account details */ + fun destination(destination: Destination) = apply { body.destination(destination) } + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Destination] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun destination(destination: JsonField) = apply { + body.destination(destination) + } + + /** Source external account details */ + fun source(source: Source) = apply { body.source(source) } + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [Source] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { body.source(source) } + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + */ + fun amount(amount: Long) = apply { body.amount(amount) } + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun amount(amount: JsonField) = apply { body.amount(amount) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TransferInCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TransferInCreateParams = + TransferInCreateParams( + idempotencyKey, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = + Headers.builder() + .apply { + idempotencyKey?.let { put("Idempotency-Key", it) } + putAll(additionalHeaders) + } + .build() + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val destination: JsonField, + private val source: JsonField, + private val amount: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("amount") @ExcludeMissing amount: JsonField = JsonMissing.of(), + ) : this(destination, source, amount, mutableMapOf()) + + /** + * Destination internal account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Destination = destination.getRequired("destination") + + /** + * Source external account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): Source = source.getRequired("source") + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun amount(): Long? = amount.getNullable("amount") + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amount") @ExcludeMissing fun _amount(): JsonField = amount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var destination: JsonField? = null + private var source: JsonField? = null + private var amount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + destination = body.destination + source = body.source + amount = body.amount + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Destination internal account details */ + fun destination(destination: Destination) = destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Destination] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Source external account details */ + fun source(source: Source) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [Source] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for + * BTC) + */ + fun amount(amount: Long) = amount(JsonField.of(amount)) + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun amount(amount: JsonField) = apply { this.amount = amount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("destination", destination), + checkRequired("source", source), + amount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + destination().validate() + source().validate() + amount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (destination.asKnown()?.validity() ?: 0) + + (source.asKnown()?.validity() ?: 0) + + (if (amount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + destination == other.destination && + source == other.source && + amount == other.amount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(destination, source, amount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{destination=$destination, source=$source, amount=$amount, additionalProperties=$additionalProperties}" + } + + /** Destination internal account details */ + class Destination + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of() + ) : this(accountId, mutableMapOf()) + + /** + * Reference to an internal account ID + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Destination]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Destination]. */ + class Builder internal constructor() { + + private var accountId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(destination: Destination) = apply { + accountId = destination.accountId + additionalProperties = destination.additionalProperties.toMutableMap() + } + + /** Reference to an internal account ID */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Destination]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Destination = + Destination( + checkRequired("accountId", accountId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Destination = apply { + if (validated) { + return@apply + } + + accountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (accountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Destination && + accountId == other.accountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(accountId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Destination{accountId=$accountId, additionalProperties=$additionalProperties}" + } + + /** Source external account details */ + class Source + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of() + ) : this(accountId, mutableMapOf()) + + /** + * Reference to an external account ID + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Source]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Source]. */ + class Builder internal constructor() { + + private var accountId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(source: Source) = apply { + accountId = source.accountId + additionalProperties = source.additionalProperties.toMutableMap() + } + + /** Reference to an external account ID */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Source]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Source = + Source(checkRequired("accountId", accountId), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Source = apply { + if (validated) { + return@apply + } + + accountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (accountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Source && + accountId == other.accountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(accountId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Source{accountId=$accountId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransferInCreateParams && + idempotencyKey == other.idempotencyKey && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(idempotencyKey, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TransferInCreateParams{idempotencyKey=$idempotencyKey, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferout/TransferOutCreateParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferout/TransferOutCreateParams.kt new file mode 100644 index 00000000..d7572ced --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/transferout/TransferOutCreateParams.kt @@ -0,0 +1,896 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferout + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.checkRequired +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +/** Transfer funds from an internal account to an external account for a specific customer. */ +class TransferOutCreateParams +private constructor( + private val idempotencyKey: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun idempotencyKey(): String? = idempotencyKey + + /** + * Destination external account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Destination = body.destination() + + /** + * Source internal account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): Source = body.source() + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun amount(): Long? = body.amount() + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _destination(): JsonField = body._destination() + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _source(): JsonField = body._source() + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _amount(): JsonField = body._amount() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [TransferOutCreateParams]. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [TransferOutCreateParams]. */ + class Builder internal constructor() { + + private var idempotencyKey: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(transferOutCreateParams: TransferOutCreateParams) = apply { + idempotencyKey = transferOutCreateParams.idempotencyKey + body = transferOutCreateParams.body.toBuilder() + additionalHeaders = transferOutCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = transferOutCreateParams.additionalQueryParams.toBuilder() + } + + fun idempotencyKey(idempotencyKey: String?) = apply { this.idempotencyKey = idempotencyKey } + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [destination] + * - [source] + * - [amount] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Destination external account details */ + fun destination(destination: Destination) = apply { body.destination(destination) } + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Destination] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun destination(destination: JsonField) = apply { + body.destination(destination) + } + + /** Source internal account details */ + fun source(source: Source) = apply { body.source(source) } + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [Source] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { body.source(source) } + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + */ + fun amount(amount: Long) = apply { body.amount(amount) } + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun amount(amount: JsonField) = apply { body.amount(amount) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [TransferOutCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): TransferOutCreateParams = + TransferOutCreateParams( + idempotencyKey, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = + Headers.builder() + .apply { + idempotencyKey?.let { put("Idempotency-Key", it) } + putAll(additionalHeaders) + } + .build() + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val destination: JsonField, + private val source: JsonField, + private val amount: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("destination") + @ExcludeMissing + destination: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("amount") @ExcludeMissing amount: JsonField = JsonMissing.of(), + ) : this(destination, source, amount, mutableMapOf()) + + /** + * Destination external account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun destination(): Destination = destination.getRequired("destination") + + /** + * Source internal account details + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun source(): Source = source.getRequired("source") + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun amount(): Long? = amount.getNullable("amount") + + /** + * Returns the raw JSON value of [destination]. + * + * Unlike [destination], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("destination") + @ExcludeMissing + fun _destination(): JsonField = destination + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * Returns the raw JSON value of [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amount") @ExcludeMissing fun _amount(): JsonField = amount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Body]. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var destination: JsonField? = null + private var source: JsonField? = null + private var amount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(body: Body) = apply { + destination = body.destination + source = body.source + amount = body.amount + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Destination external account details */ + fun destination(destination: Destination) = destination(JsonField.of(destination)) + + /** + * Sets [Builder.destination] to an arbitrary JSON value. + * + * You should usually call [Builder.destination] with a well-typed [Destination] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun destination(destination: JsonField) = apply { + this.destination = destination + } + + /** Source internal account details */ + fun source(source: Source) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [Source] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** + * Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for + * BTC) + */ + fun amount(amount: Long) = amount(JsonField.of(amount)) + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun amount(amount: JsonField) = apply { this.amount = amount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .destination() + * .source() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("destination", destination), + checkRequired("source", source), + amount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + destination().validate() + source().validate() + amount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (destination.asKnown()?.validity() ?: 0) + + (source.asKnown()?.validity() ?: 0) + + (if (amount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + destination == other.destination && + source == other.source && + amount == other.amount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(destination, source, amount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{destination=$destination, source=$source, amount=$amount, additionalProperties=$additionalProperties}" + } + + /** Destination external account details */ + class Destination + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of() + ) : this(accountId, mutableMapOf()) + + /** + * Reference to an external account ID + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Destination]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Destination]. */ + class Builder internal constructor() { + + private var accountId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(destination: Destination) = apply { + accountId = destination.accountId + additionalProperties = destination.additionalProperties.toMutableMap() + } + + /** Reference to an external account ID */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Destination]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Destination = + Destination( + checkRequired("accountId", accountId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Destination = apply { + if (validated) { + return@apply + } + + accountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (accountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Destination && + accountId == other.accountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(accountId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Destination{accountId=$accountId, additionalProperties=$additionalProperties}" + } + + /** Source internal account details */ + class Source + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accountId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accountId") + @ExcludeMissing + accountId: JsonField = JsonMissing.of() + ) : this(accountId, mutableMapOf()) + + /** + * Reference to an internal account ID + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun accountId(): String = accountId.getRequired("accountId") + + /** + * Returns the raw JSON value of [accountId]. + * + * Unlike [accountId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accountId") @ExcludeMissing fun _accountId(): JsonField = accountId + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [Source]. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [Source]. */ + class Builder internal constructor() { + + private var accountId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(source: Source) = apply { + accountId = source.accountId + additionalProperties = source.additionalProperties.toMutableMap() + } + + /** Reference to an internal account ID */ + fun accountId(accountId: String) = accountId(JsonField.of(accountId)) + + /** + * Sets [Builder.accountId] to an arbitrary JSON value. + * + * You should usually call [Builder.accountId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accountId(accountId: JsonField) = apply { this.accountId = accountId } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Source]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .accountId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Source = + Source(checkRequired("accountId", accountId), additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Source = apply { + if (validated) { + return@apply + } + + accountId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = (if (accountId.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Source && + accountId == other.accountId && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(accountId, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Source{accountId=$accountId, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is TransferOutCreateParams && + idempotencyKey == other.idempotencyKey && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(idempotencyKey, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "TransferOutCreateParams{idempotencyKey=$idempotencyKey, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPage.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPage.kt new file mode 100644 index 00000000..52c683a5 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPage.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.grid.api.core.AutoPager +import com.grid.api.core.Page +import com.grid.api.core.checkRequired +import com.grid.api.services.blocking.UmaProviderService +import java.util.Objects + +/** @see UmaProviderService.list */ +class UmaProviderListPage +private constructor( + private val service: UmaProviderService, + private val params: UmaProviderListParams, + private val response: UmaProviderListPageResponse, +) : Page { + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): UmaProviderListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override fun nextPage(): UmaProviderListPage = service.list(nextPageParams()) + + fun autoPager(): AutoPager = AutoPager.from(this) + + /** The parameters that were used to request this page. */ + fun params(): UmaProviderListParams = params + + /** The response that this page was parsed from. */ + fun response(): UmaProviderListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaProviderListPage]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaProviderListPage]. */ + class Builder internal constructor() { + + private var service: UmaProviderService? = null + private var params: UmaProviderListParams? = null + private var response: UmaProviderListPageResponse? = null + + internal fun from(umaProviderListPage: UmaProviderListPage) = apply { + service = umaProviderListPage.service + params = umaProviderListPage.params + response = umaProviderListPage.response + } + + fun service(service: UmaProviderService) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: UmaProviderListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: UmaProviderListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [UmaProviderListPage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaProviderListPage = + UmaProviderListPage( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaProviderListPage && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "UmaProviderListPage{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageAsync.kt new file mode 100644 index 00000000..dab0ee84 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageAsync.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.grid.api.core.AutoPagerAsync +import com.grid.api.core.PageAsync +import com.grid.api.core.checkRequired +import com.grid.api.services.async.UmaProviderServiceAsync +import java.util.Objects + +/** @see UmaProviderServiceAsync.list */ +class UmaProviderListPageAsync +private constructor( + private val service: UmaProviderServiceAsync, + private val params: UmaProviderListParams, + private val response: UmaProviderListPageResponse, +) : PageAsync { + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.data + */ + fun data(): List = response._data().getNullable("data") ?: emptyList() + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.nextCursor + */ + fun nextCursor(): String? = response._nextCursor().getNullable("nextCursor") + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.hasMore + */ + fun hasMore(): Boolean? = response._hasMore().getNullable("hasMore") + + /** + * Delegates to [UmaProviderListPageResponse], but gracefully handles missing data. + * + * @see UmaProviderListPageResponse.totalCount + */ + fun totalCount(): Long? = response._totalCount().getNullable("totalCount") + + override fun items(): List = data() + + override fun hasNextPage(): Boolean = items().isNotEmpty() && nextCursor() != null + + fun nextPageParams(): UmaProviderListParams { + val nextCursor = + nextCursor() ?: throw IllegalStateException("Cannot construct next page params") + return params.toBuilder().cursor(nextCursor).build() + } + + override suspend fun nextPage(): UmaProviderListPageAsync = service.list(nextPageParams()) + + fun autoPager(): AutoPagerAsync = AutoPagerAsync.from(this) + + /** The parameters that were used to request this page. */ + fun params(): UmaProviderListParams = params + + /** The response that this page was parsed from. */ + fun response(): UmaProviderListPageResponse = response + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaProviderListPageAsync]. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [UmaProviderListPageAsync]. */ + class Builder internal constructor() { + + private var service: UmaProviderServiceAsync? = null + private var params: UmaProviderListParams? = null + private var response: UmaProviderListPageResponse? = null + + internal fun from(umaProviderListPageAsync: UmaProviderListPageAsync) = apply { + service = umaProviderListPageAsync.service + params = umaProviderListPageAsync.params + response = umaProviderListPageAsync.response + } + + fun service(service: UmaProviderServiceAsync) = apply { this.service = service } + + /** The parameters that were used to request this page. */ + fun params(params: UmaProviderListParams) = apply { this.params = params } + + /** The response that this page was parsed from. */ + fun response(response: UmaProviderListPageResponse) = apply { this.response = response } + + /** + * Returns an immutable instance of [UmaProviderListPageAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .service() + * .params() + * .response() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UmaProviderListPageAsync = + UmaProviderListPageAsync( + checkRequired("service", service), + checkRequired("params", params), + checkRequired("response", response), + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaProviderListPageAsync && + service == other.service && + params == other.params && + response == other.response + } + + override fun hashCode(): Int = Objects.hash(service, params, response) + + override fun toString() = + "UmaProviderListPageAsync{service=$service, params=$params, response=$response}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponse.kt new file mode 100644 index 00000000..93f175e7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponse.kt @@ -0,0 +1,288 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class UmaProviderListPageResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField>, + private val hasMore: JsonField, + private val nextCursor: JsonField, + private val totalCount: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") + @ExcludeMissing + data: JsonField> = JsonMissing.of(), + @JsonProperty("hasMore") @ExcludeMissing hasMore: JsonField = JsonMissing.of(), + @JsonProperty("nextCursor") + @ExcludeMissing + nextCursor: JsonField = JsonMissing.of(), + @JsonProperty("totalCount") @ExcludeMissing totalCount: JsonField = JsonMissing.of(), + ) : this(data, hasMore, nextCursor, totalCount, mutableMapOf()) + + /** + * List of available UMA Providers using Grid + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun data(): List? = data.getNullable("data") + + /** + * Indicates if more results are available beyond this page + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun hasMore(): Boolean? = hasMore.getNullable("hasMore") + + /** + * Cursor to retrieve the next page of results (only present if hasMore is true) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun nextCursor(): String? = nextCursor.getNullable("nextCursor") + + /** + * Total number of transactions matching the criteria (excluding pagination) + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun totalCount(): Long? = totalCount.getNullable("totalCount") + + /** + * Returns the raw JSON value of [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") + @ExcludeMissing + fun _data(): JsonField> = data + + /** + * Returns the raw JSON value of [hasMore]. + * + * Unlike [hasMore], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("hasMore") @ExcludeMissing fun _hasMore(): JsonField = hasMore + + /** + * Returns the raw JSON value of [nextCursor]. + * + * Unlike [nextCursor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nextCursor") @ExcludeMissing fun _nextCursor(): JsonField = nextCursor + + /** + * Returns the raw JSON value of [totalCount]. + * + * Unlike [totalCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("totalCount") @ExcludeMissing fun _totalCount(): JsonField = totalCount + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UmaProviderListPageResponse]. + */ + fun builder() = Builder() + } + + /** A builder for [UmaProviderListPageResponse]. */ + class Builder internal constructor() { + + private var data: JsonField>? = null + private var hasMore: JsonField = JsonMissing.of() + private var nextCursor: JsonField = JsonMissing.of() + private var totalCount: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(umaProviderListPageResponse: UmaProviderListPageResponse) = apply { + data = umaProviderListPageResponse.data.map { it.toMutableList() } + hasMore = umaProviderListPageResponse.hasMore + nextCursor = umaProviderListPageResponse.nextCursor + totalCount = umaProviderListPageResponse.totalCount + additionalProperties = umaProviderListPageResponse.additionalProperties.toMutableMap() + } + + /** List of available UMA Providers using Grid */ + fun data(data: List) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun data(data: JsonField>) = apply { + this.data = data.map { it.toMutableList() } + } + + /** + * Adds a single [UmaProviderListResponse] to [Builder.data]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addData(data: UmaProviderListResponse) = apply { + this.data = + (this.data ?: JsonField.of(mutableListOf())).also { + checkKnown("data", it).add(data) + } + } + + /** Indicates if more results are available beyond this page */ + fun hasMore(hasMore: Boolean) = hasMore(JsonField.of(hasMore)) + + /** + * Sets [Builder.hasMore] to an arbitrary JSON value. + * + * You should usually call [Builder.hasMore] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun hasMore(hasMore: JsonField) = apply { this.hasMore = hasMore } + + /** Cursor to retrieve the next page of results (only present if hasMore is true) */ + fun nextCursor(nextCursor: String) = nextCursor(JsonField.of(nextCursor)) + + /** + * Sets [Builder.nextCursor] to an arbitrary JSON value. + * + * You should usually call [Builder.nextCursor] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun nextCursor(nextCursor: JsonField) = apply { this.nextCursor = nextCursor } + + /** Total number of transactions matching the criteria (excluding pagination) */ + fun totalCount(totalCount: Long) = totalCount(JsonField.of(totalCount)) + + /** + * Sets [Builder.totalCount] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun totalCount(totalCount: JsonField) = apply { this.totalCount = totalCount } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UmaProviderListPageResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UmaProviderListPageResponse = + UmaProviderListPageResponse( + (data ?: JsonMissing.of()).map { it.toImmutable() }, + hasMore, + nextCursor, + totalCount, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UmaProviderListPageResponse = apply { + if (validated) { + return@apply + } + + data()?.forEach { it.validate() } + hasMore() + nextCursor() + totalCount() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (data.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (if (hasMore.asKnown() == null) 0 else 1) + + (if (nextCursor.asKnown() == null) 0 else 1) + + (if (totalCount.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaProviderListPageResponse && + data == other.data && + hasMore == other.hasMore && + nextCursor == other.nextCursor && + totalCount == other.totalCount && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(data, hasMore, nextCursor, totalCount, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UmaProviderListPageResponse{data=$data, hasMore=$hasMore, nextCursor=$nextCursor, totalCount=$totalCount, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListParams.kt new file mode 100644 index 00000000..57eab670 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListParams.kt @@ -0,0 +1,419 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.fasterxml.jackson.annotation.JsonCreator +import com.grid.api.core.Enum +import com.grid.api.core.JsonField +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.errors.GridInvalidDataException +import java.util.Objects + +/** + * This endpoint provides a list of Counterparty Providers that are available. + * + * The response includes basic information about each provider, such as its UMA address, name, and + * supported currencies. This can be used to determine which providers are available for sending or + * receiving payments. + */ +class UmaProviderListParams +private constructor( + private val countryCode: String?, + private val currencyCode: String?, + private val cursor: String?, + private val hasBlockedProviders: Boolean?, + private val limit: Long?, + private val sortOrder: SortOrder?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** The alpha-2 representation of a country, as defined by the ISO 3166-1 standard. */ + fun countryCode(): String? = countryCode + + /** The ISO 4217 currency code to filter providers by supported currency. */ + fun currencyCode(): String? = currencyCode + + /** Cursor for pagination (returned from previous request) */ + fun cursor(): String? = cursor + + /** + * Whether to include providers which are not on your allowlist in the response. By default the + * response will include blocked providers. + */ + fun hasBlockedProviders(): Boolean? = hasBlockedProviders + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(): Long? = limit + + /** Order to sort results in */ + fun sortOrder(): SortOrder? = sortOrder + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): UmaProviderListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [UmaProviderListParams]. */ + fun builder() = Builder() + } + + /** A builder for [UmaProviderListParams]. */ + class Builder internal constructor() { + + private var countryCode: String? = null + private var currencyCode: String? = null + private var cursor: String? = null + private var hasBlockedProviders: Boolean? = null + private var limit: Long? = null + private var sortOrder: SortOrder? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + internal fun from(umaProviderListParams: UmaProviderListParams) = apply { + countryCode = umaProviderListParams.countryCode + currencyCode = umaProviderListParams.currencyCode + cursor = umaProviderListParams.cursor + hasBlockedProviders = umaProviderListParams.hasBlockedProviders + limit = umaProviderListParams.limit + sortOrder = umaProviderListParams.sortOrder + additionalHeaders = umaProviderListParams.additionalHeaders.toBuilder() + additionalQueryParams = umaProviderListParams.additionalQueryParams.toBuilder() + } + + /** The alpha-2 representation of a country, as defined by the ISO 3166-1 standard. */ + fun countryCode(countryCode: String?) = apply { this.countryCode = countryCode } + + /** The ISO 4217 currency code to filter providers by supported currency. */ + fun currencyCode(currencyCode: String?) = apply { this.currencyCode = currencyCode } + + /** Cursor for pagination (returned from previous request) */ + fun cursor(cursor: String?) = apply { this.cursor = cursor } + + /** + * Whether to include providers which are not on your allowlist in the response. By default + * the response will include blocked providers. + */ + fun hasBlockedProviders(hasBlockedProviders: Boolean?) = apply { + this.hasBlockedProviders = hasBlockedProviders + } + + /** + * Alias for [Builder.hasBlockedProviders]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun hasBlockedProviders(hasBlockedProviders: Boolean) = + hasBlockedProviders(hasBlockedProviders as Boolean?) + + /** Maximum number of results to return (default 20, max 100) */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Order to sort results in */ + fun sortOrder(sortOrder: SortOrder?) = apply { this.sortOrder = sortOrder } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [UmaProviderListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UmaProviderListParams = + UmaProviderListParams( + countryCode, + currencyCode, + cursor, + hasBlockedProviders, + limit, + sortOrder, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + countryCode?.let { put("countryCode", it) } + currencyCode?.let { put("currencyCode", it) } + cursor?.let { put("cursor", it) } + hasBlockedProviders?.let { put("hasBlockedProviders", it.toString()) } + limit?.let { put("limit", it.toString()) } + sortOrder?.let { put("sortOrder", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + /** Order to sort results in */ + class SortOrder @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is on an + * older version than the API, then the API may respond with new members that the SDK is + * unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + val ASC = of("asc") + + val DESC = of("desc") + + fun of(value: String) = SortOrder(JsonField.of(value)) + } + + /** An enum containing [SortOrder]'s known values. */ + enum class Known { + ASC, + DESC, + } + + /** + * An enum containing [SortOrder]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [SortOrder] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if the + * SDK is on an older version than the API, then the API may respond with new members that + * the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ASC, + DESC, + /** + * An enum member indicating that [SortOrder] was instantiated with an unknown value. + */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN] + * if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you want + * to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ASC -> Value.ASC + DESC -> Value.DESC + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws GridInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + ASC -> Known.ASC + DESC -> Known.DESC + else -> throw GridInvalidDataException("Unknown SortOrder: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for debugging + * and generally doesn't throw. + * + * @throws GridInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString() ?: throw GridInvalidDataException("Value is not a String") + + private var validated: Boolean = false + + fun validate(): SortOrder = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1 + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SortOrder && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaProviderListParams && + countryCode == other.countryCode && + currencyCode == other.currencyCode && + cursor == other.cursor && + hasBlockedProviders == other.hasBlockedProviders && + limit == other.limit && + sortOrder == other.sortOrder && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash( + countryCode, + currencyCode, + cursor, + hasBlockedProviders, + limit, + sortOrder, + additionalHeaders, + additionalQueryParams, + ) + + override fun toString() = + "UmaProviderListParams{countryCode=$countryCode, currencyCode=$currencyCode, cursor=$cursor, hasBlockedProviders=$hasBlockedProviders, limit=$limit, sortOrder=$sortOrder, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponse.kt new file mode 100644 index 00000000..5d9522db --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponse.kt @@ -0,0 +1,437 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkKnown +import com.grid.api.core.toImmutable +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.quotes.Currency +import java.util.Collections +import java.util.Objects + +class UmaProviderListResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val allowListStatus: JsonField, + private val domain: JsonField, + private val lei: JsonField, + private val logoUrl: JsonField, + private val name: JsonField, + private val supportedCurrencies: JsonField>, + private val supportedRegions: JsonField>, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("allowListStatus") + @ExcludeMissing + allowListStatus: JsonField = JsonMissing.of(), + @JsonProperty("domain") @ExcludeMissing domain: JsonField = JsonMissing.of(), + @JsonProperty("lei") @ExcludeMissing lei: JsonField = JsonMissing.of(), + @JsonProperty("logoUrl") @ExcludeMissing logoUrl: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("supportedCurrencies") + @ExcludeMissing + supportedCurrencies: JsonField> = JsonMissing.of(), + @JsonProperty("supportedRegions") + @ExcludeMissing + supportedRegions: JsonField> = JsonMissing.of(), + ) : this( + allowListStatus, + domain, + lei, + logoUrl, + name, + supportedCurrencies, + supportedRegions, + mutableMapOf(), + ) + + /** + * Whether this UMA Provider is on your allow list + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun allowListStatus(): Boolean? = allowListStatus.getNullable("allowListStatus") + + /** + * Domain this VASP uses for UMA addresses + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun domain(): String? = domain.getNullable("domain") + + /** + * Legal Entity Identifier for the UMA Provider + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun lei(): String? = lei.getNullable("lei") + + /** + * Logo URL for the VASP + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun logoUrl(): String? = logoUrl.getNullable("logoUrl") + + /** + * Name of the UMA Provider + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun name(): String? = name.getNullable("name") + + /** + * List of currencies supported by this UMA Provider + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun supportedCurrencies(): List? = + supportedCurrencies.getNullable("supportedCurrencies") + + /** + * Region(s) this UMA Provider operates in + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun supportedRegions(): List? = supportedRegions.getNullable("supportedRegions") + + /** + * Returns the raw JSON value of [allowListStatus]. + * + * Unlike [allowListStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("allowListStatus") + @ExcludeMissing + fun _allowListStatus(): JsonField = allowListStatus + + /** + * Returns the raw JSON value of [domain]. + * + * Unlike [domain], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("domain") @ExcludeMissing fun _domain(): JsonField = domain + + /** + * Returns the raw JSON value of [lei]. + * + * Unlike [lei], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lei") @ExcludeMissing fun _lei(): JsonField = lei + + /** + * Returns the raw JSON value of [logoUrl]. + * + * Unlike [logoUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("logoUrl") @ExcludeMissing fun _logoUrl(): JsonField = logoUrl + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [supportedCurrencies]. + * + * Unlike [supportedCurrencies], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("supportedCurrencies") + @ExcludeMissing + fun _supportedCurrencies(): JsonField> = supportedCurrencies + + /** + * Returns the raw JSON value of [supportedRegions]. + * + * Unlike [supportedRegions], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("supportedRegions") + @ExcludeMissing + fun _supportedRegions(): JsonField> = supportedRegions + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [UmaProviderListResponse]. */ + fun builder() = Builder() + } + + /** A builder for [UmaProviderListResponse]. */ + class Builder internal constructor() { + + private var allowListStatus: JsonField = JsonMissing.of() + private var domain: JsonField = JsonMissing.of() + private var lei: JsonField = JsonMissing.of() + private var logoUrl: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var supportedCurrencies: JsonField>? = null + private var supportedRegions: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(umaProviderListResponse: UmaProviderListResponse) = apply { + allowListStatus = umaProviderListResponse.allowListStatus + domain = umaProviderListResponse.domain + lei = umaProviderListResponse.lei + logoUrl = umaProviderListResponse.logoUrl + name = umaProviderListResponse.name + supportedCurrencies = + umaProviderListResponse.supportedCurrencies.map { it.toMutableList() } + supportedRegions = umaProviderListResponse.supportedRegions.map { it.toMutableList() } + additionalProperties = umaProviderListResponse.additionalProperties.toMutableMap() + } + + /** Whether this UMA Provider is on your allow list */ + fun allowListStatus(allowListStatus: Boolean) = + allowListStatus(JsonField.of(allowListStatus)) + + /** + * Sets [Builder.allowListStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.allowListStatus] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun allowListStatus(allowListStatus: JsonField) = apply { + this.allowListStatus = allowListStatus + } + + /** Domain this VASP uses for UMA addresses */ + fun domain(domain: String) = domain(JsonField.of(domain)) + + /** + * Sets [Builder.domain] to an arbitrary JSON value. + * + * You should usually call [Builder.domain] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun domain(domain: JsonField) = apply { this.domain = domain } + + /** Legal Entity Identifier for the UMA Provider */ + fun lei(lei: String) = lei(JsonField.of(lei)) + + /** + * Sets [Builder.lei] to an arbitrary JSON value. + * + * You should usually call [Builder.lei] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun lei(lei: JsonField) = apply { this.lei = lei } + + /** Logo URL for the VASP */ + fun logoUrl(logoUrl: String) = logoUrl(JsonField.of(logoUrl)) + + /** + * Sets [Builder.logoUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.logoUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun logoUrl(logoUrl: JsonField) = apply { this.logoUrl = logoUrl } + + /** Name of the UMA Provider */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** List of currencies supported by this UMA Provider */ + fun supportedCurrencies(supportedCurrencies: List) = + supportedCurrencies(JsonField.of(supportedCurrencies)) + + /** + * Sets [Builder.supportedCurrencies] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedCurrencies] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun supportedCurrencies(supportedCurrencies: JsonField>) = apply { + this.supportedCurrencies = supportedCurrencies.map { it.toMutableList() } + } + + /** + * Adds a single [Currency] to [supportedCurrencies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedCurrency(supportedCurrency: Currency) = apply { + supportedCurrencies = + (supportedCurrencies ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedCurrencies", it).add(supportedCurrency) + } + } + + /** Region(s) this UMA Provider operates in */ + fun supportedRegions(supportedRegions: List) = + supportedRegions(JsonField.of(supportedRegions)) + + /** + * Sets [Builder.supportedRegions] to an arbitrary JSON value. + * + * You should usually call [Builder.supportedRegions] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun supportedRegions(supportedRegions: JsonField>) = apply { + this.supportedRegions = supportedRegions.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [supportedRegions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addSupportedRegion(supportedRegion: String) = apply { + supportedRegions = + (supportedRegions ?: JsonField.of(mutableListOf())).also { + checkKnown("supportedRegions", it).add(supportedRegion) + } + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UmaProviderListResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UmaProviderListResponse = + UmaProviderListResponse( + allowListStatus, + domain, + lei, + logoUrl, + name, + (supportedCurrencies ?: JsonMissing.of()).map { it.toImmutable() }, + (supportedRegions ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): UmaProviderListResponse = apply { + if (validated) { + return@apply + } + + allowListStatus() + domain() + lei() + logoUrl() + name() + supportedCurrencies()?.forEach { it.validate() } + supportedRegions() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (allowListStatus.asKnown() == null) 0 else 1) + + (if (domain.asKnown() == null) 0 else 1) + + (if (lei.asKnown() == null) 0 else 1) + + (if (logoUrl.asKnown() == null) 0 else 1) + + (if (name.asKnown() == null) 0 else 1) + + (supportedCurrencies.asKnown()?.sumOf { it.validity().toInt() } ?: 0) + + (supportedRegions.asKnown()?.size ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UmaProviderListResponse && + allowListStatus == other.allowListStatus && + domain == other.domain && + lei == other.lei && + logoUrl == other.logoUrl && + name == other.name && + supportedCurrencies == other.supportedCurrencies && + supportedRegions == other.supportedRegions && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + allowListStatus, + domain, + lei, + logoUrl, + name, + supportedCurrencies, + supportedRegions, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UmaProviderListResponse{allowListStatus=$allowListStatus, domain=$domain, lei=$lei, logoUrl=$logoUrl, name=$name, supportedCurrencies=$supportedCurrencies, supportedRegions=$supportedRegions, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestParams.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestParams.kt new file mode 100644 index 00000000..32c5a6bd --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestParams.kt @@ -0,0 +1,207 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.webhooks + +import com.grid.api.core.JsonValue +import com.grid.api.core.Params +import com.grid.api.core.http.Headers +import com.grid.api.core.http.QueryParams +import com.grid.api.core.toImmutable +import java.util.Objects + +/** Send a test webhook to the configured endpoint */ +class WebhookSendTestParams +private constructor( + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + fun none(): WebhookSendTestParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [WebhookSendTestParams]. */ + fun builder() = Builder() + } + + /** A builder for [WebhookSendTestParams]. */ + class Builder internal constructor() { + + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + internal fun from(webhookSendTestParams: WebhookSendTestParams) = apply { + additionalHeaders = webhookSendTestParams.additionalHeaders.toBuilder() + additionalQueryParams = webhookSendTestParams.additionalQueryParams.toBuilder() + additionalBodyProperties = webhookSendTestParams.additionalBodyProperties.toMutableMap() + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [WebhookSendTestParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): WebhookSendTestParams = + WebhookSendTestParams( + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Map? = additionalBodyProperties.ifEmpty { null } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is WebhookSendTestParams && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash(additionalHeaders, additionalQueryParams, additionalBodyProperties) + + override fun toString() = + "WebhookSendTestParams{additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponse.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponse.kt new file mode 100644 index 00000000..377a3868 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponse.kt @@ -0,0 +1,257 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.webhooks + +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.grid.api.core.ExcludeMissing +import com.grid.api.core.JsonField +import com.grid.api.core.JsonMissing +import com.grid.api.core.JsonValue +import com.grid.api.core.checkRequired +import com.grid.api.errors.GridInvalidDataException +import java.util.Collections +import java.util.Objects + +class WebhookSendTestResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val responseStatus: JsonField, + private val responseBody: JsonField, + private val url: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("response_status") + @ExcludeMissing + responseStatus: JsonField = JsonMissing.of(), + @JsonProperty("response_body") + @ExcludeMissing + responseBody: JsonField = JsonMissing.of(), + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), + ) : this(responseStatus, responseBody, url, mutableMapOf()) + + /** + * The HTTP status code returned by the webhook endpoint + * + * @throws GridInvalidDataException if the JSON field has an unexpected type or is unexpectedly + * missing or null (e.g. if the server responded with an unexpected value). + */ + fun responseStatus(): Long = responseStatus.getRequired("response_status") + + /** + * The raw body content returned by the webhook endpoint in response to the request + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun responseBody(): String? = responseBody.getNullable("response_body") + + /** + * URL where the webhook was sent + * + * @throws GridInvalidDataException if the JSON field has an unexpected type (e.g. if the server + * responded with an unexpected value). + */ + fun url(): String? = url.getNullable("url") + + /** + * Returns the raw JSON value of [responseStatus]. + * + * Unlike [responseStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("response_status") + @ExcludeMissing + fun _responseStatus(): JsonField = responseStatus + + /** + * Returns the raw JSON value of [responseBody]. + * + * Unlike [responseBody], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("response_body") + @ExcludeMissing + fun _responseBody(): JsonField = responseBody + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [WebhookSendTestResponse]. + * + * The following fields are required: + * ```kotlin + * .responseStatus() + * ``` + */ + fun builder() = Builder() + } + + /** A builder for [WebhookSendTestResponse]. */ + class Builder internal constructor() { + + private var responseStatus: JsonField? = null + private var responseBody: JsonField = JsonMissing.of() + private var url: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + internal fun from(webhookSendTestResponse: WebhookSendTestResponse) = apply { + responseStatus = webhookSendTestResponse.responseStatus + responseBody = webhookSendTestResponse.responseBody + url = webhookSendTestResponse.url + additionalProperties = webhookSendTestResponse.additionalProperties.toMutableMap() + } + + /** The HTTP status code returned by the webhook endpoint */ + fun responseStatus(responseStatus: Long) = responseStatus(JsonField.of(responseStatus)) + + /** + * Sets [Builder.responseStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.responseStatus] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun responseStatus(responseStatus: JsonField) = apply { + this.responseStatus = responseStatus + } + + /** The raw body content returned by the webhook endpoint in response to the request */ + fun responseBody(responseBody: String) = responseBody(JsonField.of(responseBody)) + + /** + * Sets [Builder.responseBody] to an arbitrary JSON value. + * + * You should usually call [Builder.responseBody] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun responseBody(responseBody: JsonField) = apply { + this.responseBody = responseBody + } + + /** URL where the webhook was sent */ + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun url(url: JsonField) = apply { this.url = url } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [WebhookSendTestResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```kotlin + * .responseStatus() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): WebhookSendTestResponse = + WebhookSendTestResponse( + checkRequired("responseStatus", responseStatus), + responseBody, + url, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): WebhookSendTestResponse = apply { + if (validated) { + return@apply + } + + responseStatus() + responseBody() + url() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: GridInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + internal fun validity(): Int = + (if (responseStatus.asKnown() == null) 0 else 1) + + (if (responseBody.asKnown() == null) 0 else 1) + + (if (url.asKnown() == null) 0 else 1) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is WebhookSendTestResponse && + responseStatus == other.responseStatus && + responseBody == other.responseBody && + url == other.url && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(responseStatus, responseBody, url, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "WebhookSendTestResponse{responseStatus=$responseStatus, responseBody=$responseBody, url=$url, additionalProperties=$additionalProperties}" +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsync.kt new file mode 100644 index 00000000..f51aea5f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsync.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.config.ConfigRetrieveParams +import com.grid.api.models.config.ConfigUpdateParams +import com.grid.api.models.config.PlatformConfig + +interface ConfigServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ConfigServiceAsync + + /** Retrieve the current platform configuration */ + suspend fun retrieve( + params: ConfigRetrieveParams = ConfigRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PlatformConfig + + /** @see retrieve */ + suspend fun retrieve(requestOptions: RequestOptions): PlatformConfig = + retrieve(ConfigRetrieveParams.none(), requestOptions) + + /** Update the platform configuration settings */ + suspend fun update( + params: ConfigUpdateParams = ConfigUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PlatformConfig + + /** @see update */ + suspend fun update(requestOptions: RequestOptions): PlatformConfig = + update(ConfigUpdateParams.none(), requestOptions) + + /** + * A view of [ConfigServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ConfigServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /config`, but is otherwise the same as + * [ConfigServiceAsync.retrieve]. + */ + @MustBeClosed + suspend fun retrieve( + params: ConfigRetrieveParams = ConfigRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve(requestOptions: RequestOptions): HttpResponseFor = + retrieve(ConfigRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `patch /config`, but is otherwise the same as + * [ConfigServiceAsync.update]. + */ + @MustBeClosed + suspend fun update( + params: ConfigUpdateParams = ConfigUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see update */ + @MustBeClosed + suspend fun update(requestOptions: RequestOptions): HttpResponseFor = + update(ConfigUpdateParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsyncImpl.kt new file mode 100644 index 00000000..c1b4c078 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ConfigServiceAsyncImpl.kt @@ -0,0 +1,116 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.config.ConfigRetrieveParams +import com.grid.api.models.config.ConfigUpdateParams +import com.grid.api.models.config.PlatformConfig + +class ConfigServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + ConfigServiceAsync { + + private val withRawResponse: ConfigServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ConfigServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ConfigServiceAsync = + ConfigServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun retrieve( + params: ConfigRetrieveParams, + requestOptions: RequestOptions, + ): PlatformConfig = + // get /config + withRawResponse().retrieve(params, requestOptions).parse() + + override suspend fun update( + params: ConfigUpdateParams, + requestOptions: RequestOptions, + ): PlatformConfig = + // patch /config + withRawResponse().update(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ConfigServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ConfigServiceAsync.WithRawResponse = + ConfigServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun retrieve( + params: ConfigRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("config") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun update( + params: ConfigUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.PATCH) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("config") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsync.kt new file mode 100644 index 00000000..f926ed7b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsync.kt @@ -0,0 +1,286 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.CustomerCreateParams +import com.grid.api.models.customers.CustomerDeleteParams +import com.grid.api.models.customers.CustomerDeleteResponse +import com.grid.api.models.customers.CustomerGetKycLinkParams +import com.grid.api.models.customers.CustomerGetKycLinkResponse +import com.grid.api.models.customers.CustomerListInternalAccountsPageAsync +import com.grid.api.models.customers.CustomerListInternalAccountsParams +import com.grid.api.models.customers.CustomerListPageAsync +import com.grid.api.models.customers.CustomerListParams +import com.grid.api.models.customers.CustomerOneOf +import com.grid.api.models.customers.CustomerRetrieveParams +import com.grid.api.models.customers.CustomerRetrieveResponse +import com.grid.api.models.customers.CustomerUpdateParams +import com.grid.api.models.customers.CustomerUpdateResponse +import com.grid.api.services.async.customers.BulkServiceAsync +import com.grid.api.services.async.customers.ExternalAccountServiceAsync + +interface CustomerServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): CustomerServiceAsync + + fun externalAccounts(): ExternalAccountServiceAsync + + fun bulk(): BulkServiceAsync + + /** + * Register a new customer in the system with an account identifier and bank account information + */ + suspend fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerOneOf + + /** Retrieve a customer by their system-generated ID */ + suspend fun retrieve( + customerId: String, + params: CustomerRetrieveParams = CustomerRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerRetrieveResponse = + retrieve(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see retrieve */ + suspend fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerRetrieveResponse + + /** @see retrieve */ + suspend fun retrieve( + customerId: String, + requestOptions: RequestOptions, + ): CustomerRetrieveResponse = + retrieve(customerId, CustomerRetrieveParams.none(), requestOptions) + + /** Update a customer's metadata by their system-generated ID */ + suspend fun update( + customerId: String, + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerUpdateResponse = + update(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see update */ + suspend fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerUpdateResponse + + /** + * Retrieve a list of customers with optional filtering parameters. Returns all customers that + * match the specified filters. If no filters are provided, returns all customers (paginated). + */ + suspend fun list( + params: CustomerListParams = CustomerListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerListPageAsync + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): CustomerListPageAsync = + list(CustomerListParams.none(), requestOptions) + + /** Delete a customer by their system-generated ID */ + suspend fun delete( + customerId: String, + params: CustomerDeleteParams = CustomerDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerDeleteResponse = + delete(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see delete */ + suspend fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerDeleteResponse + + /** @see delete */ + suspend fun delete(customerId: String, requestOptions: RequestOptions): CustomerDeleteResponse = + delete(customerId, CustomerDeleteParams.none(), requestOptions) + + /** Generate a hosted KYC link to onboard a customer */ + suspend fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerGetKycLinkResponse + + /** + * Retrieve a list of internal accounts with optional filtering parameters. Returns all internal + * accounts that match the specified filters. If no filters are provided, returns all internal + * accounts (paginated). + * + * Internal accounts are created automatically when a customer is created based on the platform + * configuration. + */ + suspend fun listInternalAccounts( + params: CustomerListInternalAccountsParams = CustomerListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerListInternalAccountsPageAsync + + /** @see listInternalAccounts */ + suspend fun listInternalAccounts( + requestOptions: RequestOptions + ): CustomerListInternalAccountsPageAsync = + listInternalAccounts(CustomerListInternalAccountsParams.none(), requestOptions) + + /** + * A view of [CustomerServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): CustomerServiceAsync.WithRawResponse + + fun externalAccounts(): ExternalAccountServiceAsync.WithRawResponse + + fun bulk(): BulkServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /customers`, but is otherwise the same as + * [CustomerServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /customers/{customerId}`, but is otherwise the same + * as [CustomerServiceAsync.retrieve]. + */ + @MustBeClosed + suspend fun retrieve( + customerId: String, + params: CustomerRetrieveParams = CustomerRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + customerId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(customerId, CustomerRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `patch /customers/{customerId}`, but is otherwise the + * same as [CustomerServiceAsync.update]. + */ + @MustBeClosed + suspend fun update( + customerId: String, + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see update */ + @MustBeClosed + suspend fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /customers`, but is otherwise the same as + * [CustomerServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: CustomerListParams = CustomerListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list(requestOptions: RequestOptions): HttpResponseFor = + list(CustomerListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /customers/{customerId}`, but is otherwise the + * same as [CustomerServiceAsync.delete]. + */ + @MustBeClosed + suspend fun delete( + customerId: String, + params: CustomerDeleteParams = CustomerDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see delete */ + @MustBeClosed + suspend fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see delete */ + @MustBeClosed + suspend fun delete( + customerId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(customerId, CustomerDeleteParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /customers/kyc-link`, but is otherwise the same as + * [CustomerServiceAsync.getKycLink]. + */ + @MustBeClosed + suspend fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /customers/internal-accounts`, but is otherwise the + * same as [CustomerServiceAsync.listInternalAccounts]. + */ + @MustBeClosed + suspend fun listInternalAccounts( + params: CustomerListInternalAccountsParams = CustomerListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see listInternalAccounts */ + @MustBeClosed + suspend fun listInternalAccounts( + requestOptions: RequestOptions + ): HttpResponseFor = + listInternalAccounts(CustomerListInternalAccountsParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsyncImpl.kt new file mode 100644 index 00000000..1949cfcb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/CustomerServiceAsyncImpl.kt @@ -0,0 +1,352 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.customers.CustomerCreateParams +import com.grid.api.models.customers.CustomerDeleteParams +import com.grid.api.models.customers.CustomerDeleteResponse +import com.grid.api.models.customers.CustomerGetKycLinkParams +import com.grid.api.models.customers.CustomerGetKycLinkResponse +import com.grid.api.models.customers.CustomerListInternalAccountsPageAsync +import com.grid.api.models.customers.CustomerListInternalAccountsPageResponse +import com.grid.api.models.customers.CustomerListInternalAccountsParams +import com.grid.api.models.customers.CustomerListPageAsync +import com.grid.api.models.customers.CustomerListPageResponse +import com.grid.api.models.customers.CustomerListParams +import com.grid.api.models.customers.CustomerOneOf +import com.grid.api.models.customers.CustomerRetrieveParams +import com.grid.api.models.customers.CustomerRetrieveResponse +import com.grid.api.models.customers.CustomerUpdateParams +import com.grid.api.models.customers.CustomerUpdateResponse +import com.grid.api.services.async.customers.BulkServiceAsync +import com.grid.api.services.async.customers.BulkServiceAsyncImpl +import com.grid.api.services.async.customers.ExternalAccountServiceAsync +import com.grid.api.services.async.customers.ExternalAccountServiceAsyncImpl + +class CustomerServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + CustomerServiceAsync { + + private val withRawResponse: CustomerServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val externalAccounts: ExternalAccountServiceAsync by lazy { + ExternalAccountServiceAsyncImpl(clientOptions) + } + + private val bulk: BulkServiceAsync by lazy { BulkServiceAsyncImpl(clientOptions) } + + override fun withRawResponse(): CustomerServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): CustomerServiceAsync = + CustomerServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun externalAccounts(): ExternalAccountServiceAsync = externalAccounts + + override fun bulk(): BulkServiceAsync = bulk + + override suspend fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions, + ): CustomerOneOf = + // post /customers + withRawResponse().create(params, requestOptions).parse() + + override suspend fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions, + ): CustomerRetrieveResponse = + // get /customers/{customerId} + withRawResponse().retrieve(params, requestOptions).parse() + + override suspend fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions, + ): CustomerUpdateResponse = + // patch /customers/{customerId} + withRawResponse().update(params, requestOptions).parse() + + override suspend fun list( + params: CustomerListParams, + requestOptions: RequestOptions, + ): CustomerListPageAsync = + // get /customers + withRawResponse().list(params, requestOptions).parse() + + override suspend fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions, + ): CustomerDeleteResponse = + // delete /customers/{customerId} + withRawResponse().delete(params, requestOptions).parse() + + override suspend fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions, + ): CustomerGetKycLinkResponse = + // get /customers/kyc-link + withRawResponse().getKycLink(params, requestOptions).parse() + + override suspend fun listInternalAccounts( + params: CustomerListInternalAccountsParams, + requestOptions: RequestOptions, + ): CustomerListInternalAccountsPageAsync = + // get /customers/internal-accounts + withRawResponse().listInternalAccounts(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CustomerServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val externalAccounts: ExternalAccountServiceAsync.WithRawResponse by lazy { + ExternalAccountServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val bulk: BulkServiceAsync.WithRawResponse by lazy { + BulkServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): CustomerServiceAsync.WithRawResponse = + CustomerServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun externalAccounts(): ExternalAccountServiceAsync.WithRawResponse = + externalAccounts + + override fun bulk(): BulkServiceAsync.WithRawResponse = bulk + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("customerId", params.customerId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("customerId", params.customerId()) + val request = + HttpRequest.builder() + .method(HttpMethod.PATCH) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", params._pathParam(0)) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: CustomerListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + CustomerListPageAsync.builder() + .service(CustomerServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("customerId", params.customerId()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", params._pathParam(0)) + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val getKycLinkHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "kyc-link") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { getKycLinkHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listInternalAccountsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun listInternalAccounts( + params: CustomerListInternalAccountsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "internal-accounts") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listInternalAccountsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + CustomerListInternalAccountsPageAsync.builder() + .service(CustomerServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsync.kt new file mode 100644 index 00000000..e317d19a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsync.kt @@ -0,0 +1,77 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.exchangerates.ExchangeRateListParams +import com.grid.api.models.exchangerates.ExchangeRateListResponse + +interface ExchangeRateServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExchangeRateServiceAsync + + /** + * Retrieve cached exchange rates for currency corridors. Returns FX rates that are cached for + * approximately 5 minutes. Rates include fees specific to your platform for authenticated + * requests. + * + * **Filtering Options:** + * - Filter by source currency to get all available destination corridors + * - Filter by specific destination currency or currencies + * - Provide a sending amount to get calculated receiving amounts + */ + suspend fun list( + params: ExchangeRateListParams = ExchangeRateListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ExchangeRateListResponse + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): ExchangeRateListResponse = + list(ExchangeRateListParams.none(), requestOptions) + + /** + * A view of [ExchangeRateServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExchangeRateServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /exchange-rates`, but is otherwise the same as + * [ExchangeRateServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: ExchangeRateListParams = ExchangeRateListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list( + requestOptions: RequestOptions + ): HttpResponseFor = + list(ExchangeRateListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncImpl.kt new file mode 100644 index 00000000..d4aae681 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncImpl.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.exchangerates.ExchangeRateListParams +import com.grid.api.models.exchangerates.ExchangeRateListResponse + +class ExchangeRateServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + ExchangeRateServiceAsync { + + private val withRawResponse: ExchangeRateServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ExchangeRateServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExchangeRateServiceAsync = + ExchangeRateServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun list( + params: ExchangeRateListParams, + requestOptions: RequestOptions, + ): ExchangeRateListResponse = + // get /exchange-rates + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ExchangeRateServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExchangeRateServiceAsync.WithRawResponse = + ExchangeRateServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: ExchangeRateListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("exchange-rates") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsync.kt new file mode 100644 index 00000000..e14d7356 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsync.kt @@ -0,0 +1,203 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.invitations.InvitationCancelParams +import com.grid.api.models.invitations.InvitationClaimParams +import com.grid.api.models.invitations.InvitationCreateParams +import com.grid.api.models.invitations.InvitationRetrieveParams +import com.grid.api.models.invitations.UmaInvitation + +interface InvitationServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InvitationServiceAsync + + /** Create an UMA invitation from a given platform customer. */ + suspend fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** Get a specific UMA invitation by code. */ + suspend fun retrieve( + invitationCode: String, + params: InvitationRetrieveParams = InvitationRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation = + retrieve(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see retrieve */ + suspend fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** @see retrieve */ + suspend fun retrieve(invitationCode: String, requestOptions: RequestOptions): UmaInvitation = + retrieve(invitationCode, InvitationRetrieveParams.none(), requestOptions) + + /** + * Cancel a pending UMA invitation. Only the inviter or platform can cancel an invitation. + * + * When an invitation is cancelled: + * 1. The invitation status changes from PENDING to CANCELLED + * 2. The invitation can no longer be claimed + * 3. The invitation URL will show as cancelled when accessed + * + * Only pending invitations can be cancelled. Attempting to cancel an invitation that is already + * claimed, expired, or cancelled will result in an error. + */ + suspend fun cancel( + invitationCode: String, + params: InvitationCancelParams = InvitationCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation = + cancel(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see cancel */ + suspend fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** @see cancel */ + suspend fun cancel(invitationCode: String, requestOptions: RequestOptions): UmaInvitation = + cancel(invitationCode, InvitationCancelParams.none(), requestOptions) + + /** + * Claim an UMA invitation by associating it with an invitee UMA address. + * + * When an invitation is successfully claimed: + * 1. The invitation status changes from PENDING to CLAIMED + * 2. The invitee UMA address is associated with the invitation + * 3. An INVITATION_CLAIMED webhook is triggered to notify the platform that created the + * invitation + * + * This endpoint allows customers to accept invitations sent to them by other UMA customers. + */ + suspend fun claim( + invitationCode: String, + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation = + claim(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see claim */ + suspend fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** + * A view of [InvitationServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InvitationServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /invitations`, but is otherwise the same as + * [InvitationServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /invitations/{invitationCode}`, but is otherwise the + * same as [InvitationServiceAsync.retrieve]. + */ + @MustBeClosed + suspend fun retrieve( + invitationCode: String, + params: InvitationRetrieveParams = InvitationRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + invitationCode: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(invitationCode, InvitationRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /invitations/{invitationCode}/cancel`, but is + * otherwise the same as [InvitationServiceAsync.cancel]. + */ + @MustBeClosed + suspend fun cancel( + invitationCode: String, + params: InvitationCancelParams = InvitationCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see cancel */ + @MustBeClosed + suspend fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see cancel */ + @MustBeClosed + suspend fun cancel( + invitationCode: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + cancel(invitationCode, InvitationCancelParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /invitations/{invitationCode}/claim`, but is + * otherwise the same as [InvitationServiceAsync.claim]. + */ + @MustBeClosed + suspend fun claim( + invitationCode: String, + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + claim(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see claim */ + @MustBeClosed + suspend fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsyncImpl.kt new file mode 100644 index 00000000..e296d6c7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/InvitationServiceAsyncImpl.kt @@ -0,0 +1,198 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.invitations.InvitationCancelParams +import com.grid.api.models.invitations.InvitationClaimParams +import com.grid.api.models.invitations.InvitationCreateParams +import com.grid.api.models.invitations.InvitationRetrieveParams +import com.grid.api.models.invitations.UmaInvitation + +class InvitationServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + InvitationServiceAsync { + + private val withRawResponse: InvitationServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InvitationServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InvitationServiceAsync = + InvitationServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // post /invitations + withRawResponse().create(params, requestOptions).parse() + + override suspend fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // get /invitations/{invitationCode} + withRawResponse().retrieve(params, requestOptions).parse() + + override suspend fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // post /invitations/{invitationCode}/cancel + withRawResponse().cancel(params, requestOptions).parse() + + override suspend fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // post /invitations/{invitationCode}/claim + withRawResponse().claim(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InvitationServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InvitationServiceAsync.WithRawResponse = + InvitationServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("invitationCode", params.invitationCode()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("invitationCode", params.invitationCode()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations", params._pathParam(0), "cancel") + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val claimHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("invitationCode", params.invitationCode()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations", params._pathParam(0), "claim") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { claimHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsync.kt new file mode 100644 index 00000000..7a555bf5 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsync.kt @@ -0,0 +1,114 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.plaid.PlaidCreateLinkTokenParams +import com.grid.api.models.plaid.PlaidCreateLinkTokenResponse +import com.grid.api.models.plaid.PlaidSubmitPublicTokenParams + +interface PlaidServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlaidServiceAsync + + /** + * Creates a Plaid Link token that can be used to initialize Plaid Link in your application. The + * Link token is used to authenticate the customer and allow them to select their bank account. + * + * **Async Flow:** + * 1. Platform calls this endpoint to get a link_token and callbackUrl + * 2. Platform displays Plaid Link UI to the end customer using the link_token + * 3. End customer authenticates with their bank and selects an account + * 4. Plaid returns a public_token to the platform + * 5. Platform POSTs the public_token to the callbackUrl + * 6. Lightspark exchanges the public_token with Plaid and creates the external account + * asynchronously + * 7. Platform receives a webhook notification when the external account is ready + */ + suspend fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): PlaidCreateLinkTokenResponse + + /** + * After the customer completes Plaid Link authentication, the platform should POST the + * public_token to this callback URL (provided in the link token response). + * + * This will trigger asynchronous processing: + * 1. Lightspark exchanges the public_token for an access_token with Plaid + * 2. Lightspark retrieves and verifies the account details + * 3. An external account is created + * 4. A webhook notification is sent to the platform when complete + */ + suspend fun submitPublicToken( + plaidLinkToken: String, + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount = + submitPublicToken(params.toBuilder().plaidLinkToken(plaidLinkToken).build(), requestOptions) + + /** @see submitPublicToken */ + suspend fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount + + /** A view of [PlaidServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): PlaidServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /plaid/link-tokens`, but is otherwise the same as + * [PlaidServiceAsync.createLinkToken]. + */ + @MustBeClosed + suspend fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /plaid/callback/{plaid_link_token}`, but is + * otherwise the same as [PlaidServiceAsync.submitPublicToken]. + */ + @MustBeClosed + suspend fun submitPublicToken( + plaidLinkToken: String, + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + submitPublicToken( + params.toBuilder().plaidLinkToken(plaidLinkToken).build(), + requestOptions, + ) + + /** @see submitPublicToken */ + @MustBeClosed + suspend fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsyncImpl.kt new file mode 100644 index 00000000..206068da --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlaidServiceAsyncImpl.kt @@ -0,0 +1,122 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.plaid.PlaidCreateLinkTokenParams +import com.grid.api.models.plaid.PlaidCreateLinkTokenResponse +import com.grid.api.models.plaid.PlaidSubmitPublicTokenParams + +class PlaidServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + PlaidServiceAsync { + + private val withRawResponse: PlaidServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): PlaidServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlaidServiceAsync = + PlaidServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions, + ): PlaidCreateLinkTokenResponse = + // post /plaid/link-tokens + withRawResponse().createLinkToken(params, requestOptions).parse() + + override suspend fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions, + ): ExternalAccount = + // post /plaid/callback/{plaid_link_token} + withRawResponse().submitPublicToken(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + PlaidServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): PlaidServiceAsync.WithRawResponse = + PlaidServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createLinkTokenHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("plaid", "link-tokens") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createLinkTokenHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val submitPublicTokenHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("plaidLinkToken", params.plaidLinkToken()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("plaid", "callback", params._pathParam(0)) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { submitPublicTokenHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsync.kt new file mode 100644 index 00000000..165e25ae --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsync.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.platform.PlatformListInternalAccountsParams +import com.grid.api.models.platform.PlatformListInternalAccountsResponse +import com.grid.api.services.async.platform.ExternalAccountServiceAsync + +interface PlatformServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlatformServiceAsync + + fun externalAccounts(): ExternalAccountServiceAsync + + /** + * Retrieve a list of all internal accounts that belong to the platform, as opposed to an + * individual customer. + * + * These accounts are created automatically when the platform is configured for each supported + * currency. They can be used for things like distributing bitcoin rewards to customers, or for + * other platform-wide purposes. + */ + suspend fun listInternalAccounts( + params: PlatformListInternalAccountsParams = PlatformListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PlatformListInternalAccountsResponse + + /** @see listInternalAccounts */ + suspend fun listInternalAccounts( + requestOptions: RequestOptions + ): PlatformListInternalAccountsResponse = + listInternalAccounts(PlatformListInternalAccountsParams.none(), requestOptions) + + /** + * A view of [PlatformServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): PlatformServiceAsync.WithRawResponse + + fun externalAccounts(): ExternalAccountServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /platform/internal-accounts`, but is otherwise the + * same as [PlatformServiceAsync.listInternalAccounts]. + */ + @MustBeClosed + suspend fun listInternalAccounts( + params: PlatformListInternalAccountsParams = PlatformListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see listInternalAccounts */ + @MustBeClosed + suspend fun listInternalAccounts( + requestOptions: RequestOptions + ): HttpResponseFor = + listInternalAccounts(PlatformListInternalAccountsParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsyncImpl.kt new file mode 100644 index 00000000..17f5b00d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/PlatformServiceAsyncImpl.kt @@ -0,0 +1,94 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.platform.PlatformListInternalAccountsParams +import com.grid.api.models.platform.PlatformListInternalAccountsResponse +import com.grid.api.services.async.platform.ExternalAccountServiceAsync +import com.grid.api.services.async.platform.ExternalAccountServiceAsyncImpl + +class PlatformServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + PlatformServiceAsync { + + private val withRawResponse: PlatformServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val externalAccounts: ExternalAccountServiceAsync by lazy { + ExternalAccountServiceAsyncImpl(clientOptions) + } + + override fun withRawResponse(): PlatformServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlatformServiceAsync = + PlatformServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun externalAccounts(): ExternalAccountServiceAsync = externalAccounts + + override suspend fun listInternalAccounts( + params: PlatformListInternalAccountsParams, + requestOptions: RequestOptions, + ): PlatformListInternalAccountsResponse = + // get /platform/internal-accounts + withRawResponse().listInternalAccounts(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + PlatformServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val externalAccounts: ExternalAccountServiceAsync.WithRawResponse by lazy { + ExternalAccountServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): PlatformServiceAsync.WithRawResponse = + PlatformServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun externalAccounts(): ExternalAccountServiceAsync.WithRawResponse = + externalAccounts + + private val listInternalAccountsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun listInternalAccounts( + params: PlatformListInternalAccountsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("platform", "internal-accounts") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listInternalAccountsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsync.kt new file mode 100644 index 00000000..ecf912c1 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsync.kt @@ -0,0 +1,215 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteExecuteParams +import com.grid.api.models.quotes.QuoteListPageAsync +import com.grid.api.models.quotes.QuoteListParams +import com.grid.api.models.quotes.QuoteRetrieveParams + +interface QuoteServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): QuoteServiceAsync + + /** + * Generate a quote for a cross-currency transfer between any combination of accounts and UMA + * addresses. This endpoint handles currency exchange and provides the necessary instructions to + * execute the transfer. + * + * **Transfer Types Supported:** + * - **Account to Account**: Transfer between internal/external accounts with currency exchange. + * - **Account to UMA**: Transfer from an internal account to an UMA address. + * - **UMA to Account or UMA to UMA**: This transfer type will only be funded by payment + * instructions, not from an internal account. + * + * **Key Features:** + * - **Flexible Amount Locking**: Always specify whether you want to lock the sending amount or + * receiving amount + * - **Currency Exchange**: Handles all cross-currency transfers with real-time exchange rates + * - **Payment Instructions**: For UMA or customer ID sources, provides banking details needed + * for execution + * + * **Important:** If you are transferring funds in the same currency (no exchange required), use + * the `/transfer-in` or `/transfer-out` endpoints instead. + * + * **Sandbox Testing:** When using the `externalAccountDetails` destination type in sandbox + * mode, use account number patterns ending in specific digits to test different scenarios. + * These patterns should be used with the primary alias, address, or identifier of whatever + * account type you're testing. For example, the US account number, a CLABE, an IBAN, a spark + * wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ + suspend fun create( + params: QuoteCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote + + /** + * Retrieve a quote by its ID. If the quote has been settled, it will include the transaction + * ID. This allows clients to track the full lifecycle of a payment from quote creation to + * settlement. + */ + suspend fun retrieve( + quoteId: String, + params: QuoteRetrieveParams = QuoteRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote = retrieve(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see retrieve */ + suspend fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote + + /** @see retrieve */ + suspend fun retrieve(quoteId: String, requestOptions: RequestOptions): Quote = + retrieve(quoteId, QuoteRetrieveParams.none(), requestOptions) + + /** + * Retrieve a list of transfer quotes with optional filtering parameters. Returns all quotes + * that match the specified filters. If no filters are provided, returns all quotes (paginated). + */ + suspend fun list( + params: QuoteListParams = QuoteListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): QuoteListPageAsync + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): QuoteListPageAsync = + list(QuoteListParams.none(), requestOptions) + + /** + * Execute a quote by its ID. This endpoint initiates the transfer between the source and + * destination accounts. + * + * This endpoint can only be used for quotes with a `source` which is either an internal + * account, or has direct pull functionality (e.g. ACH pull with an external account). + * + * Once executed, the quote cannot be cancelled and the transfer will be processed. + */ + suspend fun execute( + quoteId: String, + params: QuoteExecuteParams = QuoteExecuteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote = execute(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see execute */ + suspend fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote + + /** @see execute */ + suspend fun execute(quoteId: String, requestOptions: RequestOptions): Quote = + execute(quoteId, QuoteExecuteParams.none(), requestOptions) + + /** A view of [QuoteServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): QuoteServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /quotes`, but is otherwise the same as + * [QuoteServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: QuoteCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /quotes/{quoteId}`, but is otherwise the same as + * [QuoteServiceAsync.retrieve]. + */ + @MustBeClosed + suspend fun retrieve( + quoteId: String, + params: QuoteRetrieveParams = QuoteRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + quoteId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = retrieve(quoteId, QuoteRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /quotes`, but is otherwise the same as + * [QuoteServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: QuoteListParams = QuoteListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list(requestOptions: RequestOptions): HttpResponseFor = + list(QuoteListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /quotes/{quoteId}/execute`, but is otherwise the + * same as [QuoteServiceAsync.execute]. + */ + @MustBeClosed + suspend fun execute( + quoteId: String, + params: QuoteExecuteParams = QuoteExecuteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + execute(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see execute */ + @MustBeClosed + suspend fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see execute */ + @MustBeClosed + suspend fun execute( + quoteId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = execute(quoteId, QuoteExecuteParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsyncImpl.kt new file mode 100644 index 00000000..a0810956 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/QuoteServiceAsyncImpl.kt @@ -0,0 +1,197 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteExecuteParams +import com.grid.api.models.quotes.QuoteListPageAsync +import com.grid.api.models.quotes.QuoteListPageResponse +import com.grid.api.models.quotes.QuoteListParams +import com.grid.api.models.quotes.QuoteRetrieveParams + +class QuoteServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + QuoteServiceAsync { + + private val withRawResponse: QuoteServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): QuoteServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): QuoteServiceAsync = + QuoteServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create(params: QuoteCreateParams, requestOptions: RequestOptions): Quote = + // post /quotes + withRawResponse().create(params, requestOptions).parse() + + override suspend fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions, + ): Quote = + // get /quotes/{quoteId} + withRawResponse().retrieve(params, requestOptions).parse() + + override suspend fun list( + params: QuoteListParams, + requestOptions: RequestOptions, + ): QuoteListPageAsync = + // get /quotes + withRawResponse().list(params, requestOptions).parse() + + override suspend fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions, + ): Quote = + // post /quotes/{quoteId}/execute + withRawResponse().execute(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + QuoteServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): QuoteServiceAsync.WithRawResponse = + QuoteServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: QuoteCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = jsonHandler(clientOptions.jsonMapper) + + override suspend fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("quoteId", params.quoteId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: QuoteListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + QuoteListPageAsync.builder() + .service(QuoteServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val executeHandler: Handler = jsonHandler(clientOptions.jsonMapper) + + override suspend fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("quoteId", params.quoteId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes", params._pathParam(0), "execute") + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { executeHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsync.kt new file mode 100644 index 00000000..5e0868b0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsync.kt @@ -0,0 +1,154 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.receiver.ReceiverLookupExternalAccountParams +import com.grid.api.models.receiver.ReceiverLookupExternalAccountResponse +import com.grid.api.models.receiver.ReceiverLookupUmaParams +import com.grid.api.models.receiver.ReceiverLookupUmaResponse + +interface ReceiverServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ReceiverServiceAsync + + /** + * Lookup an external account by ID to determine supported currencies and exchange rates. This + * endpoint helps platforms determine what currencies they can send to a given external account, + * along with the current estimated exchange rates and minimum and maximum amounts that can be + * sent. + */ + suspend fun lookupExternalAccount( + accountId: String, + params: ReceiverLookupExternalAccountParams = ReceiverLookupExternalAccountParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupExternalAccountResponse = + lookupExternalAccount(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see lookupExternalAccount */ + suspend fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupExternalAccountResponse + + /** @see lookupExternalAccount */ + suspend fun lookupExternalAccount( + accountId: String, + requestOptions: RequestOptions, + ): ReceiverLookupExternalAccountResponse = + lookupExternalAccount(accountId, ReceiverLookupExternalAccountParams.none(), requestOptions) + + /** + * Lookup a receiving UMA address to determine supported currencies and exchange rates. This + * endpoint helps platforms determine what currencies they can send to a given UMA address. + */ + suspend fun lookupUma( + receiverUmaAddress: String, + params: ReceiverLookupUmaParams = ReceiverLookupUmaParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupUmaResponse = + lookupUma(params.toBuilder().receiverUmaAddress(receiverUmaAddress).build(), requestOptions) + + /** @see lookupUma */ + suspend fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupUmaResponse + + /** @see lookupUma */ + suspend fun lookupUma( + receiverUmaAddress: String, + requestOptions: RequestOptions, + ): ReceiverLookupUmaResponse = + lookupUma(receiverUmaAddress, ReceiverLookupUmaParams.none(), requestOptions) + + /** + * A view of [ReceiverServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ReceiverServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /receiver/external-account/{accountId}`, but is + * otherwise the same as [ReceiverServiceAsync.lookupExternalAccount]. + */ + @MustBeClosed + suspend fun lookupExternalAccount( + accountId: String, + params: ReceiverLookupExternalAccountParams = + ReceiverLookupExternalAccountParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + lookupExternalAccount(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see lookupExternalAccount */ + @MustBeClosed + suspend fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see lookupExternalAccount */ + @MustBeClosed + suspend fun lookupExternalAccount( + accountId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + lookupExternalAccount( + accountId, + ReceiverLookupExternalAccountParams.none(), + requestOptions, + ) + + /** + * Returns a raw HTTP response for `get /receiver/uma/{receiverUmaAddress}`, but is + * otherwise the same as [ReceiverServiceAsync.lookupUma]. + */ + @MustBeClosed + suspend fun lookupUma( + receiverUmaAddress: String, + params: ReceiverLookupUmaParams = ReceiverLookupUmaParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + lookupUma( + params.toBuilder().receiverUmaAddress(receiverUmaAddress).build(), + requestOptions, + ) + + /** @see lookupUma */ + @MustBeClosed + suspend fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see lookupUma */ + @MustBeClosed + suspend fun lookupUma( + receiverUmaAddress: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + lookupUma(receiverUmaAddress, ReceiverLookupUmaParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsyncImpl.kt new file mode 100644 index 00000000..085f2e9d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/ReceiverServiceAsyncImpl.kt @@ -0,0 +1,122 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.receiver.ReceiverLookupExternalAccountParams +import com.grid.api.models.receiver.ReceiverLookupExternalAccountResponse +import com.grid.api.models.receiver.ReceiverLookupUmaParams +import com.grid.api.models.receiver.ReceiverLookupUmaResponse + +class ReceiverServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + ReceiverServiceAsync { + + private val withRawResponse: ReceiverServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ReceiverServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ReceiverServiceAsync = + ReceiverServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions, + ): ReceiverLookupExternalAccountResponse = + // get /receiver/external-account/{accountId} + withRawResponse().lookupExternalAccount(params, requestOptions).parse() + + override suspend fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions, + ): ReceiverLookupUmaResponse = + // get /receiver/uma/{receiverUmaAddress} + withRawResponse().lookupUma(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ReceiverServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ReceiverServiceAsync.WithRawResponse = + ReceiverServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val lookupExternalAccountHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("accountId", params.accountId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("receiver", "external-account", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { lookupExternalAccountHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val lookupUmaHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("receiverUmaAddress", params.receiverUmaAddress()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("receiver", "uma", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { lookupUmaHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsync.kt new file mode 100644 index 00000000..6c320693 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsync.kt @@ -0,0 +1,69 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.sandbox.SandboxSendFundsParams +import com.grid.api.models.sandbox.SandboxSendFundsResponse +import com.grid.api.services.async.sandbox.InternalAccountServiceAsync +import com.grid.api.services.async.sandbox.UmaServiceAsync + +interface SandboxServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): SandboxServiceAsync + + fun uma(): UmaServiceAsync + + fun internalAccounts(): InternalAccountServiceAsync + + /** + * Simulate sending funds to the bank account as instructed in the quote. This endpoint is only + * for the sandbox environment and will fail for production platforms/keys. + */ + suspend fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): SandboxSendFundsResponse + + /** + * A view of [SandboxServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): SandboxServiceAsync.WithRawResponse + + fun uma(): UmaServiceAsync.WithRawResponse + + fun internalAccounts(): InternalAccountServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /sandbox/send`, but is otherwise the same as + * [SandboxServiceAsync.sendFunds]. + */ + @MustBeClosed + suspend fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsyncImpl.kt new file mode 100644 index 00000000..39bc3786 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/SandboxServiceAsyncImpl.kt @@ -0,0 +1,108 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.sandbox.SandboxSendFundsParams +import com.grid.api.models.sandbox.SandboxSendFundsResponse +import com.grid.api.services.async.sandbox.InternalAccountServiceAsync +import com.grid.api.services.async.sandbox.InternalAccountServiceAsyncImpl +import com.grid.api.services.async.sandbox.UmaServiceAsync +import com.grid.api.services.async.sandbox.UmaServiceAsyncImpl + +class SandboxServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + SandboxServiceAsync { + + private val withRawResponse: SandboxServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val uma: UmaServiceAsync by lazy { UmaServiceAsyncImpl(clientOptions) } + + private val internalAccounts: InternalAccountServiceAsync by lazy { + InternalAccountServiceAsyncImpl(clientOptions) + } + + override fun withRawResponse(): SandboxServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): SandboxServiceAsync = + SandboxServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun uma(): UmaServiceAsync = uma + + override fun internalAccounts(): InternalAccountServiceAsync = internalAccounts + + override suspend fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions, + ): SandboxSendFundsResponse = + // post /sandbox/send + withRawResponse().sendFunds(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + SandboxServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val uma: UmaServiceAsync.WithRawResponse by lazy { + UmaServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val internalAccounts: InternalAccountServiceAsync.WithRawResponse by lazy { + InternalAccountServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): SandboxServiceAsync.WithRawResponse = + SandboxServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun uma(): UmaServiceAsync.WithRawResponse = uma + + override fun internalAccounts(): InternalAccountServiceAsync.WithRawResponse = + internalAccounts + + private val sendFundsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("sandbox", "send") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { sendFundsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsync.kt new file mode 100644 index 00000000..a304916b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsync.kt @@ -0,0 +1,170 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.tokens.ApiToken +import com.grid.api.models.tokens.TokenCreateParams +import com.grid.api.models.tokens.TokenDeleteParams +import com.grid.api.models.tokens.TokenListPageAsync +import com.grid.api.models.tokens.TokenListParams +import com.grid.api.models.tokens.TokenRetrieveParams + +interface TokenServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TokenServiceAsync + + /** Create a new API token to access the Grid APIs. */ + suspend fun create( + params: TokenCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiToken + + /** Retrieve an API token by their system-generated ID */ + suspend fun retrieve( + tokenId: String, + params: TokenRetrieveParams = TokenRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiToken = retrieve(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see retrieve */ + suspend fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiToken + + /** @see retrieve */ + suspend fun retrieve(tokenId: String, requestOptions: RequestOptions): ApiToken = + retrieve(tokenId, TokenRetrieveParams.none(), requestOptions) + + /** + * Retrieve a list of API tokens with optional filtering parameters. Returns all tokens that + * match the specified filters. If no filters are provided, returns all tokens (paginated). + */ + suspend fun list( + params: TokenListParams = TokenListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): TokenListPageAsync + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): TokenListPageAsync = + list(TokenListParams.none(), requestOptions) + + /** Delete an API token by their system-generated ID */ + suspend fun delete( + tokenId: String, + params: TokenDeleteParams = TokenDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ) = delete(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see delete */ + suspend fun delete( + params: TokenDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ) + + /** @see delete */ + suspend fun delete(tokenId: String, requestOptions: RequestOptions) = + delete(tokenId, TokenDeleteParams.none(), requestOptions) + + /** A view of [TokenServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TokenServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /tokens`, but is otherwise the same as + * [TokenServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: TokenCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /tokens/{tokenId}`, but is otherwise the same as + * [TokenServiceAsync.retrieve]. + */ + @MustBeClosed + suspend fun retrieve( + tokenId: String, + params: TokenRetrieveParams = TokenRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + tokenId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = retrieve(tokenId, TokenRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /tokens`, but is otherwise the same as + * [TokenServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: TokenListParams = TokenListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list(requestOptions: RequestOptions): HttpResponseFor = + list(TokenListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /tokens/{tokenId}`, but is otherwise the same as + * [TokenServiceAsync.delete]. + */ + @MustBeClosed + suspend fun delete( + tokenId: String, + params: TokenDeleteParams = TokenDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = delete(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see delete */ + @MustBeClosed + suspend fun delete( + params: TokenDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + /** @see delete */ + @MustBeClosed + suspend fun delete(tokenId: String, requestOptions: RequestOptions): HttpResponse = + delete(tokenId, TokenDeleteParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsyncImpl.kt new file mode 100644 index 00000000..757578c0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TokenServiceAsyncImpl.kt @@ -0,0 +1,195 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.emptyHandler +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.tokens.ApiToken +import com.grid.api.models.tokens.TokenCreateParams +import com.grid.api.models.tokens.TokenDeleteParams +import com.grid.api.models.tokens.TokenListPageAsync +import com.grid.api.models.tokens.TokenListPageResponse +import com.grid.api.models.tokens.TokenListParams +import com.grid.api.models.tokens.TokenRetrieveParams + +class TokenServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + TokenServiceAsync { + + private val withRawResponse: TokenServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TokenServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TokenServiceAsync = + TokenServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create( + params: TokenCreateParams, + requestOptions: RequestOptions, + ): ApiToken = + // post /tokens + withRawResponse().create(params, requestOptions).parse() + + override suspend fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions, + ): ApiToken = + // get /tokens/{tokenId} + withRawResponse().retrieve(params, requestOptions).parse() + + override suspend fun list( + params: TokenListParams, + requestOptions: RequestOptions, + ): TokenListPageAsync = + // get /tokens + withRawResponse().list(params, requestOptions).parse() + + override suspend fun delete(params: TokenDeleteParams, requestOptions: RequestOptions) { + // delete /tokens/{tokenId} + withRawResponse().delete(params, requestOptions) + } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TokenServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TokenServiceAsync.WithRawResponse = + TokenServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: TokenCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("tokenId", params.tokenId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: TokenListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + TokenListPageAsync.builder() + .service(TokenServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val deleteHandler: Handler = emptyHandler() + + override suspend fun delete( + params: TokenDeleteParams, + requestOptions: RequestOptions, + ): HttpResponse { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("tokenId", params.tokenId()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens", params._pathParam(0)) + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response.use { deleteHandler.handle(it) } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsync.kt new file mode 100644 index 00000000..5fc95dba --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsync.kt @@ -0,0 +1,220 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.transactions.IncomingTransaction +import com.grid.api.models.transactions.TransactionApproveParams +import com.grid.api.models.transactions.TransactionListPageAsync +import com.grid.api.models.transactions.TransactionListParams +import com.grid.api.models.transactions.TransactionRejectParams +import com.grid.api.models.transactions.TransactionRetrieveParams +import com.grid.api.models.transferin.Transaction + +interface TransactionServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransactionServiceAsync + + /** Retrieve detailed information about a specific transaction */ + suspend fun retrieve( + transactionId: String, + params: TransactionRetrieveParams = TransactionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction = + retrieve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see retrieve */ + suspend fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction + + /** @see retrieve */ + suspend fun retrieve(transactionId: String, requestOptions: RequestOptions): Transaction = + retrieve(transactionId, TransactionRetrieveParams.none(), requestOptions) + + /** + * Retrieve a paginated list of transactions with optional filtering. The transactions can be + * filtered by customer ID, platform customer ID, UMA address, date range, status, and + * transaction type. + */ + suspend fun list( + params: TransactionListParams = TransactionListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): TransactionListPageAsync + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): TransactionListPageAsync = + list(TransactionListParams.none(), requestOptions) + + /** + * Approve a pending incoming payment that was previously acknowledged with a 202 response. This + * endpoint allows platforms to asynchronously approve payments after async processing. + */ + suspend fun approve( + transactionId: String, + params: TransactionApproveParams = TransactionApproveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction = + approve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see approve */ + suspend fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction + + /** @see approve */ + suspend fun approve( + transactionId: String, + requestOptions: RequestOptions, + ): IncomingTransaction = approve(transactionId, TransactionApproveParams.none(), requestOptions) + + /** + * Reject a pending incoming payment that was previously acknowledged with a 202 response. This + * endpoint allows platforms to asynchronously reject payments after additional processing. + */ + suspend fun reject( + transactionId: String, + params: TransactionRejectParams = TransactionRejectParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction = + reject(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see reject */ + suspend fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction + + /** @see reject */ + suspend fun reject(transactionId: String, requestOptions: RequestOptions): IncomingTransaction = + reject(transactionId, TransactionRejectParams.none(), requestOptions) + + /** + * A view of [TransactionServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransactionServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /transactions/{transactionId}`, but is otherwise the + * same as [TransactionServiceAsync.retrieve]. + */ + @MustBeClosed + suspend fun retrieve( + transactionId: String, + params: TransactionRetrieveParams = TransactionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + suspend fun retrieve( + transactionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(transactionId, TransactionRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /transactions`, but is otherwise the same as + * [TransactionServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: TransactionListParams = TransactionListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list( + requestOptions: RequestOptions + ): HttpResponseFor = + list(TransactionListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /transactions/{transactionId}/approve`, but is + * otherwise the same as [TransactionServiceAsync.approve]. + */ + @MustBeClosed + suspend fun approve( + transactionId: String, + params: TransactionApproveParams = TransactionApproveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + approve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see approve */ + @MustBeClosed + suspend fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see approve */ + @MustBeClosed + suspend fun approve( + transactionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + approve(transactionId, TransactionApproveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /transactions/{transactionId}/reject`, but is + * otherwise the same as [TransactionServiceAsync.reject]. + */ + @MustBeClosed + suspend fun reject( + transactionId: String, + params: TransactionRejectParams = TransactionRejectParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + reject(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see reject */ + @MustBeClosed + suspend fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see reject */ + @MustBeClosed + suspend fun reject( + transactionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + reject(transactionId, TransactionRejectParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsyncImpl.kt new file mode 100644 index 00000000..7678edb3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransactionServiceAsyncImpl.kt @@ -0,0 +1,207 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.transactions.IncomingTransaction +import com.grid.api.models.transactions.TransactionApproveParams +import com.grid.api.models.transactions.TransactionListPageAsync +import com.grid.api.models.transactions.TransactionListPageResponse +import com.grid.api.models.transactions.TransactionListParams +import com.grid.api.models.transactions.TransactionRejectParams +import com.grid.api.models.transactions.TransactionRetrieveParams +import com.grid.api.models.transferin.Transaction + +class TransactionServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + TransactionServiceAsync { + + private val withRawResponse: TransactionServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TransactionServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransactionServiceAsync = + TransactionServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions, + ): Transaction = + // get /transactions/{transactionId} + withRawResponse().retrieve(params, requestOptions).parse() + + override suspend fun list( + params: TransactionListParams, + requestOptions: RequestOptions, + ): TransactionListPageAsync = + // get /transactions + withRawResponse().list(params, requestOptions).parse() + + override suspend fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions, + ): IncomingTransaction = + // post /transactions/{transactionId}/approve + withRawResponse().approve(params, requestOptions).parse() + + override suspend fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions, + ): IncomingTransaction = + // post /transactions/{transactionId}/reject + withRawResponse().reject(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TransactionServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransactionServiceAsync.WithRawResponse = + TransactionServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("transactionId", params.transactionId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: TransactionListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + TransactionListPageAsync.builder() + .service(TransactionServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val approveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("transactionId", params.transactionId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions", params._pathParam(0), "approve") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { approveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val rejectHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("transactionId", params.transactionId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions", params._pathParam(0), "reject") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { rejectHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsync.kt new file mode 100644 index 00000000..bf6f7d24 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsync.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferin.TransferInCreateParams + +interface TransferInServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferInServiceAsync + + /** + * Transfer funds from an external account to an internal account for a specific customer. This + * endpoint should only be used for external account sources with pull functionality (e.g. ACH + * Pull). Otherwise, use the paymentInstructions on the internal account to deposit funds. + */ + suspend fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction + + /** + * A view of [TransferInServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferInServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /transfer-in`, but is otherwise the same as + * [TransferInServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsyncImpl.kt new file mode 100644 index 00000000..763167f5 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferInServiceAsyncImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferin.TransferInCreateParams + +class TransferInServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + TransferInServiceAsync { + + private val withRawResponse: TransferInServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TransferInServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferInServiceAsync = + TransferInServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions, + ): Transaction = + // post /transfer-in + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TransferInServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferInServiceAsync.WithRawResponse = + TransferInServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transfer-in") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsync.kt new file mode 100644 index 00000000..f425b810 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsync.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferout.TransferOutCreateParams + +interface TransferOutServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferOutServiceAsync + + /** Transfer funds from an internal account to an external account for a specific customer. */ + suspend fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction + + /** + * A view of [TransferOutServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferOutServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /transfer-out`, but is otherwise the same as + * [TransferOutServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsyncImpl.kt new file mode 100644 index 00000000..fd9c2d17 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/TransferOutServiceAsyncImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferout.TransferOutCreateParams + +class TransferOutServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + TransferOutServiceAsync { + + private val withRawResponse: TransferOutServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TransferOutServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferOutServiceAsync = + TransferOutServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions, + ): Transaction = + // post /transfer-out + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TransferOutServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferOutServiceAsync.WithRawResponse = + TransferOutServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transfer-out") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsync.kt new file mode 100644 index 00000000..f0728ea9 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsync.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.umaproviders.UmaProviderListPageAsync +import com.grid.api.models.umaproviders.UmaProviderListParams + +interface UmaProviderServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaProviderServiceAsync + + /** + * This endpoint provides a list of Counterparty Providers that are available. + * + * The response includes basic information about each provider, such as its UMA address, name, + * and supported currencies. This can be used to determine which providers are available for + * sending or receiving payments. + */ + suspend fun list( + params: UmaProviderListParams = UmaProviderListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaProviderListPageAsync + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): UmaProviderListPageAsync = + list(UmaProviderListParams.none(), requestOptions) + + /** + * A view of [UmaProviderServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): UmaProviderServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /uma-providers`, but is otherwise the same as + * [UmaProviderServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: UmaProviderListParams = UmaProviderListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list( + requestOptions: RequestOptions + ): HttpResponseFor = + list(UmaProviderListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncImpl.kt new file mode 100644 index 00000000..5191159b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.umaproviders.UmaProviderListPageAsync +import com.grid.api.models.umaproviders.UmaProviderListPageResponse +import com.grid.api.models.umaproviders.UmaProviderListParams + +class UmaProviderServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + UmaProviderServiceAsync { + + private val withRawResponse: UmaProviderServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): UmaProviderServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaProviderServiceAsync = + UmaProviderServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun list( + params: UmaProviderListParams, + requestOptions: RequestOptions, + ): UmaProviderListPageAsync = + // get /uma-providers + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + UmaProviderServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): UmaProviderServiceAsync.WithRawResponse = + UmaProviderServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: UmaProviderListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("uma-providers") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + UmaProviderListPageAsync.builder() + .service(UmaProviderServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsync.kt new file mode 100644 index 00000000..e9d53260 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsync.kt @@ -0,0 +1,67 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.webhooks.WebhookSendTestParams +import com.grid.api.models.webhooks.WebhookSendTestResponse + +interface WebhookServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): WebhookServiceAsync + + /** Send a test webhook to the configured endpoint */ + suspend fun sendTest( + params: WebhookSendTestParams = WebhookSendTestParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): WebhookSendTestResponse + + /** @see sendTest */ + suspend fun sendTest(requestOptions: RequestOptions): WebhookSendTestResponse = + sendTest(WebhookSendTestParams.none(), requestOptions) + + /** + * A view of [WebhookServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): WebhookServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /webhooks/test`, but is otherwise the same as + * [WebhookServiceAsync.sendTest]. + */ + @MustBeClosed + suspend fun sendTest( + params: WebhookSendTestParams = WebhookSendTestParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see sendTest */ + @MustBeClosed + suspend fun sendTest( + requestOptions: RequestOptions + ): HttpResponseFor = + sendTest(WebhookSendTestParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsyncImpl.kt new file mode 100644 index 00000000..0bc8cbf8 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/WebhookServiceAsyncImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.webhooks.WebhookSendTestParams +import com.grid.api.models.webhooks.WebhookSendTestResponse + +class WebhookServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + WebhookServiceAsync { + + private val withRawResponse: WebhookServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): WebhookServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): WebhookServiceAsync = + WebhookServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun sendTest( + params: WebhookSendTestParams, + requestOptions: RequestOptions, + ): WebhookSendTestResponse = + // post /webhooks/test + withRawResponse().sendTest(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + WebhookServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): WebhookServiceAsync.WithRawResponse = + WebhookServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val sendTestHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun sendTest( + params: WebhookSendTestParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("webhooks", "test") + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { sendTestHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsync.kt new file mode 100644 index 00000000..9d687c59 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsync.kt @@ -0,0 +1,161 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.customers + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.bulk.BulkGetJobStatusParams +import com.grid.api.models.customers.bulk.BulkGetJobStatusResponse +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse + +interface BulkServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): BulkServiceAsync + + /** + * Retrieve the current status and results of a bulk customer import job. This endpoint can be + * used to track the progress of both CSV uploads. + * + * The response includes: + * - Overall job status + * - Progress statistics + * - Detailed error information for failed entries + * - Completion timestamp when finished + */ + suspend fun getJobStatus( + jobId: String, + params: BulkGetJobStatusParams = BulkGetJobStatusParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): BulkGetJobStatusResponse = + getJobStatus(params.toBuilder().jobId(jobId).build(), requestOptions) + + /** @see getJobStatus */ + suspend fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): BulkGetJobStatusResponse + + /** @see getJobStatus */ + suspend fun getJobStatus( + jobId: String, + requestOptions: RequestOptions, + ): BulkGetJobStatusResponse = getJobStatus(jobId, BulkGetJobStatusParams.none(), requestOptions) + + /** + * Upload a CSV file containing customer information for bulk creation. The CSV file should + * follow a specific format with required and optional columns based on customer type. + * + * ### CSV Format + * The CSV file should have the following columns: + * + * Required columns for all customers: + * - umaAddress: The customer's UMA address (e.g., $john.doe@uma.domain.com) + * - platformCustomerId: Your platform's unique identifier for the customer + * - customerType: Either "INDIVIDUAL" or "BUSINESS" + * + * Required columns for individual customers: + * - fullName: Individual's full name + * - birthDate: Date of birth in YYYY-MM-DD format + * - addressLine1: Street address line 1 + * - city: City + * - state: State/Province/Region + * - postalCode: Postal/ZIP code + * - country: Country code (ISO 3166-1 alpha-2) + * + * Required columns for business customers: + * - businessLegalName: Legal name of the business + * - addressLine1: Street address line 1 + * - city: City + * - state: State/Province/Region + * - postalCode: Postal/ZIP code + * - country: Country code (ISO 3166-1 alpha-2) + * + * Optional columns for all customers: + * - addressLine2: Street address line 2 + * - platformAccountId: Your platform's identifier for the bank account + * - description: Optional description for the customer + * + * Optional columns for individual customers: + * - email: Customer's email address + * + * Optional columns for business customers: + * - businessRegistrationNumber: Business registration number + * - businessTaxId: Tax identification number + * + * ### Example CSV + * + * ```csv + * umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName + * john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US + * acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US + * ``` + * + * The upload process is asynchronous and will return a job ID that can be used to track + * progress. You can monitor the job status using the `/customers/bulk/jobs/{jobId}` endpoint. + */ + suspend fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): BulkUploadCsvResponse + + /** A view of [BulkServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): BulkServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `get /customers/bulk/jobs/{jobId}`, but is otherwise the + * same as [BulkServiceAsync.getJobStatus]. + */ + @MustBeClosed + suspend fun getJobStatus( + jobId: String, + params: BulkGetJobStatusParams = BulkGetJobStatusParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + getJobStatus(params.toBuilder().jobId(jobId).build(), requestOptions) + + /** @see getJobStatus */ + @MustBeClosed + suspend fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see getJobStatus */ + @MustBeClosed + suspend fun getJobStatus( + jobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + getJobStatus(jobId, BulkGetJobStatusParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /customers/bulk/csv`, but is otherwise the same as + * [BulkServiceAsync.uploadCsv]. + */ + @MustBeClosed + suspend fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncImpl.kt new file mode 100644 index 00000000..ba9dbd8d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncImpl.kt @@ -0,0 +1,121 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.customers + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.multipartFormData +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.customers.bulk.BulkGetJobStatusParams +import com.grid.api.models.customers.bulk.BulkGetJobStatusResponse +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse + +class BulkServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + BulkServiceAsync { + + private val withRawResponse: BulkServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): BulkServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): BulkServiceAsync = + BulkServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions, + ): BulkGetJobStatusResponse = + // get /customers/bulk/jobs/{jobId} + withRawResponse().getJobStatus(params, requestOptions).parse() + + override suspend fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions, + ): BulkUploadCsvResponse = + // post /customers/bulk/csv + withRawResponse().uploadCsv(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + BulkServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): BulkServiceAsync.WithRawResponse = + BulkServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val getJobStatusHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("jobId", params.jobId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "bulk", "jobs", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { getJobStatusHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val uploadCsvHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "bulk", "csv") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { uploadCsvHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsync.kt new file mode 100644 index 00000000..6383cb7b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsync.kt @@ -0,0 +1,131 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.customers + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.customers.externalaccounts.ExternalAccountListPageAsync +import com.grid.api.models.customers.externalaccounts.ExternalAccountListParams + +interface ExternalAccountServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExternalAccountServiceAsync + + /** + * Register a new external bank account for a customer. + * + * **Sandbox Testing:** In sandbox mode, use these account number patterns to test different + * transfer scenarios. These patterns should be used with the primary alias, address, or + * identifier of whatever account type you're testing. For example, the US account number, a + * CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ + suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount + + /** @see create */ + suspend fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Retrieve a list of external accounts with optional filtering parameters. Returns all external + * accounts that match the specified filters. If no filters are provided, returns all external + * accounts (paginated). + * + * External accounts are bank accounts, cryptocurrency wallets, or other payment destinations + * that customers can use to receive funds from the platform. + */ + suspend fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccountListPageAsync + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): ExternalAccountListPageAsync = + list(ExternalAccountListParams.none(), requestOptions) + + /** + * A view of [ExternalAccountServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /customers/external-accounts`, but is otherwise the + * same as [ExternalAccountServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + suspend fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Returns a raw HTTP response for `get /customers/external-accounts`, but is otherwise the + * same as [ExternalAccountServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list( + requestOptions: RequestOptions + ): HttpResponseFor = + list(ExternalAccountListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncImpl.kt new file mode 100644 index 00000000..4fb6ca15 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncImpl.kt @@ -0,0 +1,127 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.customers + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.customers.externalaccounts.ExternalAccountListPageAsync +import com.grid.api.models.customers.externalaccounts.ExternalAccountListPageResponse +import com.grid.api.models.customers.externalaccounts.ExternalAccountListParams + +class ExternalAccountServiceAsyncImpl +internal constructor(private val clientOptions: ClientOptions) : ExternalAccountServiceAsync { + + private val withRawResponse: ExternalAccountServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ExternalAccountServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountServiceAsync = + ExternalAccountServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): ExternalAccount = + // post /customers/external-accounts + withRawResponse().create(params, requestOptions).parse() + + override suspend fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): ExternalAccountListPageAsync = + // get /customers/external-accounts + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ExternalAccountServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountServiceAsync.WithRawResponse = + ExternalAccountServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "external-accounts") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "external-accounts") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + ExternalAccountListPageAsync.builder() + .service(ExternalAccountServiceAsyncImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsync.kt new file mode 100644 index 00000000..5a9221e0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsync.kt @@ -0,0 +1,130 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.platform + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.platform.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListResponse + +interface ExternalAccountServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExternalAccountServiceAsync + + /** + * Register a new external bank account for the platform. + * + * **Sandbox Testing:** In sandbox mode, use these account number patterns to test different + * transfer scenarios. These patterns should be used with the primary alias, address, or + * identifier of whatever account type you're testing. For example, the US account number, a + * CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ + suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount + + /** @see create */ + suspend fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Retrieve a list of all external accounts that belong to the platform, as opposed to an + * individual customer. + * + * These accounts are used for platform-wide operations such as receiving funds from external + * sources or managing platform-level payment destinations. + */ + suspend fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccountListResponse + + /** @see list */ + suspend fun list(requestOptions: RequestOptions): ExternalAccountListResponse = + list(ExternalAccountListParams.none(), requestOptions) + + /** + * A view of [ExternalAccountServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /platform/external-accounts`, but is otherwise the + * same as [ExternalAccountServiceAsync.create]. + */ + @MustBeClosed + suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + suspend fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Returns a raw HTTP response for `get /platform/external-accounts`, but is otherwise the + * same as [ExternalAccountServiceAsync.list]. + */ + @MustBeClosed + suspend fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + suspend fun list( + requestOptions: RequestOptions + ): HttpResponseFor = + list(ExternalAccountListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncImpl.kt new file mode 100644 index 00000000..f1f303d0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncImpl.kt @@ -0,0 +1,119 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.platform + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.platform.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListResponse + +class ExternalAccountServiceAsyncImpl +internal constructor(private val clientOptions: ClientOptions) : ExternalAccountServiceAsync { + + private val withRawResponse: ExternalAccountServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ExternalAccountServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountServiceAsync = + ExternalAccountServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): ExternalAccount = + // post /platform/external-accounts + withRawResponse().create(params, requestOptions).parse() + + override suspend fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): ExternalAccountListResponse = + // get /platform/external-accounts + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ExternalAccountServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountServiceAsync.WithRawResponse = + ExternalAccountServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("platform", "external-accounts") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("platform", "external-accounts") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsync.kt new file mode 100644 index 00000000..8f7becdf --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsync.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.sandbox + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import com.grid.api.models.sandbox.internalaccounts.InternalAccountFundParams + +interface InternalAccountServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InternalAccountServiceAsync + + /** + * Simulate receiving funds into an internal account in the sandbox environment. This is useful + * for testing scenarios where you need to add funds to a customer's or platform's internal + * account without going through a real bank transfer or following payment instructions. This + * endpoint is only for the sandbox environment and will fail for production platforms/keys. + */ + suspend fun fund( + accountId: String, + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InternalAccount = fund(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see fund */ + suspend fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InternalAccount + + /** + * A view of [InternalAccountServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InternalAccountServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /sandbox/internal-accounts/{accountId}/fund`, but + * is otherwise the same as [InternalAccountServiceAsync.fund]. + */ + @MustBeClosed + suspend fun fund( + accountId: String, + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + fund(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see fund */ + @MustBeClosed + suspend fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncImpl.kt new file mode 100644 index 00000000..5ccc6283 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.sandbox + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import com.grid.api.models.sandbox.internalaccounts.InternalAccountFundParams + +class InternalAccountServiceAsyncImpl +internal constructor(private val clientOptions: ClientOptions) : InternalAccountServiceAsync { + + private val withRawResponse: InternalAccountServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InternalAccountServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InternalAccountServiceAsync = + InternalAccountServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions, + ): InternalAccount = + // post /sandbox/internal-accounts/{accountId}/fund + withRawResponse().fund(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InternalAccountServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InternalAccountServiceAsync.WithRawResponse = + InternalAccountServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val fundHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("accountId", params.accountId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("sandbox", "internal-accounts", params._pathParam(0), "fund") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { fundHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsync.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsync.kt new file mode 100644 index 00000000..3d91864a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsync.kt @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.sandbox + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.sandbox.uma.UmaReceivePaymentParams +import com.grid.api.models.transactions.IncomingTransaction + +interface UmaServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaServiceAsync + + /** + * Simulate sending payment from an sandbox uma address to a platform customer to test payment + * receive. This endpoint is only for the sandbox environment and will fail for production + * platforms/keys. + */ + suspend fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction + + /** A view of [UmaServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /sandbox/uma/receive`, but is otherwise the same as + * [UmaServiceAsync.receivePayment]. + */ + @MustBeClosed + suspend fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncImpl.kt new file mode 100644 index 00000000..82006847 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.sandbox + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepareAsync +import com.grid.api.models.sandbox.uma.UmaReceivePaymentParams +import com.grid.api.models.transactions.IncomingTransaction + +class UmaServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + UmaServiceAsync { + + private val withRawResponse: UmaServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): UmaServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaServiceAsync = + UmaServiceAsyncImpl(clientOptions.toBuilder().apply(modifier).build()) + + override suspend fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions, + ): IncomingTransaction = + // post /sandbox/uma/receive + withRawResponse().receivePayment(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + UmaServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): UmaServiceAsync.WithRawResponse = + UmaServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val receivePaymentHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override suspend fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("sandbox", "uma", "receive") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.executeAsync(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { receivePaymentHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigService.kt new file mode 100644 index 00000000..5e1bb397 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigService.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.config.ConfigRetrieveParams +import com.grid.api.models.config.ConfigUpdateParams +import com.grid.api.models.config.PlatformConfig + +interface ConfigService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ConfigService + + /** Retrieve the current platform configuration */ + fun retrieve( + params: ConfigRetrieveParams = ConfigRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PlatformConfig + + /** @see retrieve */ + fun retrieve(requestOptions: RequestOptions): PlatformConfig = + retrieve(ConfigRetrieveParams.none(), requestOptions) + + /** Update the platform configuration settings */ + fun update( + params: ConfigUpdateParams = ConfigUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PlatformConfig + + /** @see update */ + fun update(requestOptions: RequestOptions): PlatformConfig = + update(ConfigUpdateParams.none(), requestOptions) + + /** A view of [ConfigService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ConfigService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /config`, but is otherwise the same as + * [ConfigService.retrieve]. + */ + @MustBeClosed + fun retrieve( + params: ConfigRetrieveParams = ConfigRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve(requestOptions: RequestOptions): HttpResponseFor = + retrieve(ConfigRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `patch /config`, but is otherwise the same as + * [ConfigService.update]. + */ + @MustBeClosed + fun update( + params: ConfigUpdateParams = ConfigUpdateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see update */ + @MustBeClosed + fun update(requestOptions: RequestOptions): HttpResponseFor = + update(ConfigUpdateParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigServiceImpl.kt new file mode 100644 index 00000000..369eb73d --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ConfigServiceImpl.kt @@ -0,0 +1,114 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.config.ConfigRetrieveParams +import com.grid.api.models.config.ConfigUpdateParams +import com.grid.api.models.config.PlatformConfig + +class ConfigServiceImpl internal constructor(private val clientOptions: ClientOptions) : + ConfigService { + + private val withRawResponse: ConfigService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ConfigService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ConfigService = + ConfigServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun retrieve( + params: ConfigRetrieveParams, + requestOptions: RequestOptions, + ): PlatformConfig = + // get /config + withRawResponse().retrieve(params, requestOptions).parse() + + override fun update( + params: ConfigUpdateParams, + requestOptions: RequestOptions, + ): PlatformConfig = + // patch /config + withRawResponse().update(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ConfigService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ConfigService.WithRawResponse = + ConfigServiceImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: ConfigRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("config") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun update( + params: ConfigUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.PATCH) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("config") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerService.kt new file mode 100644 index 00000000..bb03af48 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerService.kt @@ -0,0 +1,277 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.CustomerCreateParams +import com.grid.api.models.customers.CustomerDeleteParams +import com.grid.api.models.customers.CustomerDeleteResponse +import com.grid.api.models.customers.CustomerGetKycLinkParams +import com.grid.api.models.customers.CustomerGetKycLinkResponse +import com.grid.api.models.customers.CustomerListInternalAccountsPage +import com.grid.api.models.customers.CustomerListInternalAccountsParams +import com.grid.api.models.customers.CustomerListPage +import com.grid.api.models.customers.CustomerListParams +import com.grid.api.models.customers.CustomerOneOf +import com.grid.api.models.customers.CustomerRetrieveParams +import com.grid.api.models.customers.CustomerRetrieveResponse +import com.grid.api.models.customers.CustomerUpdateParams +import com.grid.api.models.customers.CustomerUpdateResponse +import com.grid.api.services.blocking.customers.BulkService +import com.grid.api.services.blocking.customers.ExternalAccountService + +interface CustomerService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): CustomerService + + fun externalAccounts(): ExternalAccountService + + fun bulk(): BulkService + + /** + * Register a new customer in the system with an account identifier and bank account information + */ + fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerOneOf + + /** Retrieve a customer by their system-generated ID */ + fun retrieve( + customerId: String, + params: CustomerRetrieveParams = CustomerRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerRetrieveResponse = + retrieve(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerRetrieveResponse + + /** @see retrieve */ + fun retrieve(customerId: String, requestOptions: RequestOptions): CustomerRetrieveResponse = + retrieve(customerId, CustomerRetrieveParams.none(), requestOptions) + + /** Update a customer's metadata by their system-generated ID */ + fun update( + customerId: String, + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerUpdateResponse = + update(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see update */ + fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerUpdateResponse + + /** + * Retrieve a list of customers with optional filtering parameters. Returns all customers that + * match the specified filters. If no filters are provided, returns all customers (paginated). + */ + fun list( + params: CustomerListParams = CustomerListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerListPage + + /** @see list */ + fun list(requestOptions: RequestOptions): CustomerListPage = + list(CustomerListParams.none(), requestOptions) + + /** Delete a customer by their system-generated ID */ + fun delete( + customerId: String, + params: CustomerDeleteParams = CustomerDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerDeleteResponse = + delete(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see delete */ + fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerDeleteResponse + + /** @see delete */ + fun delete(customerId: String, requestOptions: RequestOptions): CustomerDeleteResponse = + delete(customerId, CustomerDeleteParams.none(), requestOptions) + + /** Generate a hosted KYC link to onboard a customer */ + fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerGetKycLinkResponse + + /** + * Retrieve a list of internal accounts with optional filtering parameters. Returns all internal + * accounts that match the specified filters. If no filters are provided, returns all internal + * accounts (paginated). + * + * Internal accounts are created automatically when a customer is created based on the platform + * configuration. + */ + fun listInternalAccounts( + params: CustomerListInternalAccountsParams = CustomerListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CustomerListInternalAccountsPage + + /** @see listInternalAccounts */ + fun listInternalAccounts(requestOptions: RequestOptions): CustomerListInternalAccountsPage = + listInternalAccounts(CustomerListInternalAccountsParams.none(), requestOptions) + + /** A view of [CustomerService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): CustomerService.WithRawResponse + + fun externalAccounts(): ExternalAccountService.WithRawResponse + + fun bulk(): BulkService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /customers`, but is otherwise the same as + * [CustomerService.create]. + */ + @MustBeClosed + fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /customers/{customerId}`, but is otherwise the same + * as [CustomerService.retrieve]. + */ + @MustBeClosed + fun retrieve( + customerId: String, + params: CustomerRetrieveParams = CustomerRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + customerId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(customerId, CustomerRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `patch /customers/{customerId}`, but is otherwise the + * same as [CustomerService.update]. + */ + @MustBeClosed + fun update( + customerId: String, + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see update */ + @MustBeClosed + fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /customers`, but is otherwise the same as + * [CustomerService.list]. + */ + @MustBeClosed + fun list( + params: CustomerListParams = CustomerListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(CustomerListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /customers/{customerId}`, but is otherwise the + * same as [CustomerService.delete]. + */ + @MustBeClosed + fun delete( + customerId: String, + params: CustomerDeleteParams = CustomerDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().customerId(customerId).build(), requestOptions) + + /** @see delete */ + @MustBeClosed + fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see delete */ + @MustBeClosed + fun delete( + customerId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(customerId, CustomerDeleteParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /customers/kyc-link`, but is otherwise the same as + * [CustomerService.getKycLink]. + */ + @MustBeClosed + fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /customers/internal-accounts`, but is otherwise the + * same as [CustomerService.listInternalAccounts]. + */ + @MustBeClosed + fun listInternalAccounts( + params: CustomerListInternalAccountsParams = CustomerListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see listInternalAccounts */ + @MustBeClosed + fun listInternalAccounts( + requestOptions: RequestOptions + ): HttpResponseFor = + listInternalAccounts(CustomerListInternalAccountsParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerServiceImpl.kt new file mode 100644 index 00000000..37c91f3a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/CustomerServiceImpl.kt @@ -0,0 +1,351 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.customers.CustomerCreateParams +import com.grid.api.models.customers.CustomerDeleteParams +import com.grid.api.models.customers.CustomerDeleteResponse +import com.grid.api.models.customers.CustomerGetKycLinkParams +import com.grid.api.models.customers.CustomerGetKycLinkResponse +import com.grid.api.models.customers.CustomerListInternalAccountsPage +import com.grid.api.models.customers.CustomerListInternalAccountsPageResponse +import com.grid.api.models.customers.CustomerListInternalAccountsParams +import com.grid.api.models.customers.CustomerListPage +import com.grid.api.models.customers.CustomerListPageResponse +import com.grid.api.models.customers.CustomerListParams +import com.grid.api.models.customers.CustomerOneOf +import com.grid.api.models.customers.CustomerRetrieveParams +import com.grid.api.models.customers.CustomerRetrieveResponse +import com.grid.api.models.customers.CustomerUpdateParams +import com.grid.api.models.customers.CustomerUpdateResponse +import com.grid.api.services.blocking.customers.BulkService +import com.grid.api.services.blocking.customers.BulkServiceImpl +import com.grid.api.services.blocking.customers.ExternalAccountService +import com.grid.api.services.blocking.customers.ExternalAccountServiceImpl + +class CustomerServiceImpl internal constructor(private val clientOptions: ClientOptions) : + CustomerService { + + private val withRawResponse: CustomerService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val externalAccounts: ExternalAccountService by lazy { + ExternalAccountServiceImpl(clientOptions) + } + + private val bulk: BulkService by lazy { BulkServiceImpl(clientOptions) } + + override fun withRawResponse(): CustomerService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): CustomerService = + CustomerServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun externalAccounts(): ExternalAccountService = externalAccounts + + override fun bulk(): BulkService = bulk + + override fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions, + ): CustomerOneOf = + // post /customers + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions, + ): CustomerRetrieveResponse = + // get /customers/{customerId} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions, + ): CustomerUpdateResponse = + // patch /customers/{customerId} + withRawResponse().update(params, requestOptions).parse() + + override fun list( + params: CustomerListParams, + requestOptions: RequestOptions, + ): CustomerListPage = + // get /customers + withRawResponse().list(params, requestOptions).parse() + + override fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions, + ): CustomerDeleteResponse = + // delete /customers/{customerId} + withRawResponse().delete(params, requestOptions).parse() + + override fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions, + ): CustomerGetKycLinkResponse = + // get /customers/kyc-link + withRawResponse().getKycLink(params, requestOptions).parse() + + override fun listInternalAccounts( + params: CustomerListInternalAccountsParams, + requestOptions: RequestOptions, + ): CustomerListInternalAccountsPage = + // get /customers/internal-accounts + withRawResponse().listInternalAccounts(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CustomerService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val externalAccounts: ExternalAccountService.WithRawResponse by lazy { + ExternalAccountServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val bulk: BulkService.WithRawResponse by lazy { + BulkServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): CustomerService.WithRawResponse = + CustomerServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun externalAccounts(): ExternalAccountService.WithRawResponse = externalAccounts + + override fun bulk(): BulkService.WithRawResponse = bulk + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: CustomerCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: CustomerRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("customerId", params.customerId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun update( + params: CustomerUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("customerId", params.customerId()) + val request = + HttpRequest.builder() + .method(HttpMethod.PATCH) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", params._pathParam(0)) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: CustomerListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + CustomerListPage.builder() + .service(CustomerServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun delete( + params: CustomerDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("customerId", params.customerId()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", params._pathParam(0)) + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val getKycLinkHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun getKycLink( + params: CustomerGetKycLinkParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "kyc-link") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { getKycLinkHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listInternalAccountsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun listInternalAccounts( + params: CustomerListInternalAccountsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "internal-accounts") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listInternalAccountsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + CustomerListInternalAccountsPage.builder() + .service(CustomerServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateService.kt new file mode 100644 index 00000000..22d7e438 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateService.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.exchangerates.ExchangeRateListParams +import com.grid.api.models.exchangerates.ExchangeRateListResponse + +interface ExchangeRateService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExchangeRateService + + /** + * Retrieve cached exchange rates for currency corridors. Returns FX rates that are cached for + * approximately 5 minutes. Rates include fees specific to your platform for authenticated + * requests. + * + * **Filtering Options:** + * - Filter by source currency to get all available destination corridors + * - Filter by specific destination currency or currencies + * - Provide a sending amount to get calculated receiving amounts + */ + fun list( + params: ExchangeRateListParams = ExchangeRateListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ExchangeRateListResponse + + /** @see list */ + fun list(requestOptions: RequestOptions): ExchangeRateListResponse = + list(ExchangeRateListParams.none(), requestOptions) + + /** + * A view of [ExchangeRateService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExchangeRateService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /exchange-rates`, but is otherwise the same as + * [ExchangeRateService.list]. + */ + @MustBeClosed + fun list( + params: ExchangeRateListParams = ExchangeRateListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(ExchangeRateListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateServiceImpl.kt new file mode 100644 index 00000000..285efbcb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ExchangeRateServiceImpl.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.exchangerates.ExchangeRateListParams +import com.grid.api.models.exchangerates.ExchangeRateListResponse + +class ExchangeRateServiceImpl internal constructor(private val clientOptions: ClientOptions) : + ExchangeRateService { + + private val withRawResponse: ExchangeRateService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ExchangeRateService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExchangeRateService = + ExchangeRateServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun list( + params: ExchangeRateListParams, + requestOptions: RequestOptions, + ): ExchangeRateListResponse = + // get /exchange-rates + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ExchangeRateService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExchangeRateService.WithRawResponse = + ExchangeRateServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: ExchangeRateListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("exchange-rates") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationService.kt new file mode 100644 index 00000000..fad44bd2 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationService.kt @@ -0,0 +1,200 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.invitations.InvitationCancelParams +import com.grid.api.models.invitations.InvitationClaimParams +import com.grid.api.models.invitations.InvitationCreateParams +import com.grid.api.models.invitations.InvitationRetrieveParams +import com.grid.api.models.invitations.UmaInvitation + +interface InvitationService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InvitationService + + /** Create an UMA invitation from a given platform customer. */ + fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** Get a specific UMA invitation by code. */ + fun retrieve( + invitationCode: String, + params: InvitationRetrieveParams = InvitationRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation = + retrieve(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** @see retrieve */ + fun retrieve(invitationCode: String, requestOptions: RequestOptions): UmaInvitation = + retrieve(invitationCode, InvitationRetrieveParams.none(), requestOptions) + + /** + * Cancel a pending UMA invitation. Only the inviter or platform can cancel an invitation. + * + * When an invitation is cancelled: + * 1. The invitation status changes from PENDING to CANCELLED + * 2. The invitation can no longer be claimed + * 3. The invitation URL will show as cancelled when accessed + * + * Only pending invitations can be cancelled. Attempting to cancel an invitation that is already + * claimed, expired, or cancelled will result in an error. + */ + fun cancel( + invitationCode: String, + params: InvitationCancelParams = InvitationCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation = + cancel(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see cancel */ + fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** @see cancel */ + fun cancel(invitationCode: String, requestOptions: RequestOptions): UmaInvitation = + cancel(invitationCode, InvitationCancelParams.none(), requestOptions) + + /** + * Claim an UMA invitation by associating it with an invitee UMA address. + * + * When an invitation is successfully claimed: + * 1. The invitation status changes from PENDING to CLAIMED + * 2. The invitee UMA address is associated with the invitation + * 3. An INVITATION_CLAIMED webhook is triggered to notify the platform that created the + * invitation + * + * This endpoint allows customers to accept invitations sent to them by other UMA customers. + */ + fun claim( + invitationCode: String, + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation = + claim(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see claim */ + fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaInvitation + + /** A view of [InvitationService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InvitationService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /invitations`, but is otherwise the same as + * [InvitationService.create]. + */ + @MustBeClosed + fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /invitations/{invitationCode}`, but is otherwise the + * same as [InvitationService.retrieve]. + */ + @MustBeClosed + fun retrieve( + invitationCode: String, + params: InvitationRetrieveParams = InvitationRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + invitationCode: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(invitationCode, InvitationRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /invitations/{invitationCode}/cancel`, but is + * otherwise the same as [InvitationService.cancel]. + */ + @MustBeClosed + fun cancel( + invitationCode: String, + params: InvitationCancelParams = InvitationCancelParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + cancel(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see cancel */ + @MustBeClosed + fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see cancel */ + @MustBeClosed + fun cancel( + invitationCode: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + cancel(invitationCode, InvitationCancelParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /invitations/{invitationCode}/claim`, but is + * otherwise the same as [InvitationService.claim]. + */ + @MustBeClosed + fun claim( + invitationCode: String, + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + claim(params.toBuilder().invitationCode(invitationCode).build(), requestOptions) + + /** @see claim */ + @MustBeClosed + fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationServiceImpl.kt new file mode 100644 index 00000000..86162ab8 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/InvitationServiceImpl.kt @@ -0,0 +1,198 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.invitations.InvitationCancelParams +import com.grid.api.models.invitations.InvitationClaimParams +import com.grid.api.models.invitations.InvitationCreateParams +import com.grid.api.models.invitations.InvitationRetrieveParams +import com.grid.api.models.invitations.UmaInvitation + +class InvitationServiceImpl internal constructor(private val clientOptions: ClientOptions) : + InvitationService { + + private val withRawResponse: InvitationService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InvitationService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InvitationService = + InvitationServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // post /invitations + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // get /invitations/{invitationCode} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // post /invitations/{invitationCode}/cancel + withRawResponse().cancel(params, requestOptions).parse() + + override fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions, + ): UmaInvitation = + // post /invitations/{invitationCode}/claim + withRawResponse().claim(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InvitationService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InvitationService.WithRawResponse = + InvitationServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: InvitationCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: InvitationRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("invitationCode", params.invitationCode()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val cancelHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun cancel( + params: InvitationCancelParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("invitationCode", params.invitationCode()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations", params._pathParam(0), "cancel") + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { cancelHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val claimHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun claim( + params: InvitationClaimParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("invitationCode", params.invitationCode()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("invitations", params._pathParam(0), "claim") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { claimHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidService.kt new file mode 100644 index 00000000..9c9696a7 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidService.kt @@ -0,0 +1,112 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.plaid.PlaidCreateLinkTokenParams +import com.grid.api.models.plaid.PlaidCreateLinkTokenResponse +import com.grid.api.models.plaid.PlaidSubmitPublicTokenParams + +interface PlaidService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlaidService + + /** + * Creates a Plaid Link token that can be used to initialize Plaid Link in your application. The + * Link token is used to authenticate the customer and allow them to select their bank account. + * + * **Async Flow:** + * 1. Platform calls this endpoint to get a link_token and callbackUrl + * 2. Platform displays Plaid Link UI to the end customer using the link_token + * 3. End customer authenticates with their bank and selects an account + * 4. Plaid returns a public_token to the platform + * 5. Platform POSTs the public_token to the callbackUrl + * 6. Lightspark exchanges the public_token with Plaid and creates the external account + * asynchronously + * 7. Platform receives a webhook notification when the external account is ready + */ + fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): PlaidCreateLinkTokenResponse + + /** + * After the customer completes Plaid Link authentication, the platform should POST the + * public_token to this callback URL (provided in the link token response). + * + * This will trigger asynchronous processing: + * 1. Lightspark exchanges the public_token for an access_token with Plaid + * 2. Lightspark retrieves and verifies the account details + * 3. An external account is created + * 4. A webhook notification is sent to the platform when complete + */ + fun submitPublicToken( + plaidLinkToken: String, + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount = + submitPublicToken(params.toBuilder().plaidLinkToken(plaidLinkToken).build(), requestOptions) + + /** @see submitPublicToken */ + fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount + + /** A view of [PlaidService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlaidService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /plaid/link-tokens`, but is otherwise the same as + * [PlaidService.createLinkToken]. + */ + @MustBeClosed + fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /plaid/callback/{plaid_link_token}`, but is + * otherwise the same as [PlaidService.submitPublicToken]. + */ + @MustBeClosed + fun submitPublicToken( + plaidLinkToken: String, + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + submitPublicToken( + params.toBuilder().plaidLinkToken(plaidLinkToken).build(), + requestOptions, + ) + + /** @see submitPublicToken */ + @MustBeClosed + fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidServiceImpl.kt new file mode 100644 index 00000000..ae23b5d3 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlaidServiceImpl.kt @@ -0,0 +1,120 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.plaid.PlaidCreateLinkTokenParams +import com.grid.api.models.plaid.PlaidCreateLinkTokenResponse +import com.grid.api.models.plaid.PlaidSubmitPublicTokenParams + +class PlaidServiceImpl internal constructor(private val clientOptions: ClientOptions) : + PlaidService { + + private val withRawResponse: PlaidService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): PlaidService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlaidService = + PlaidServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions, + ): PlaidCreateLinkTokenResponse = + // post /plaid/link-tokens + withRawResponse().createLinkToken(params, requestOptions).parse() + + override fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions, + ): ExternalAccount = + // post /plaid/callback/{plaid_link_token} + withRawResponse().submitPublicToken(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + PlaidService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): PlaidService.WithRawResponse = + PlaidServiceImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + private val createLinkTokenHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun createLinkToken( + params: PlaidCreateLinkTokenParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("plaid", "link-tokens") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createLinkTokenHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val submitPublicTokenHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun submitPublicToken( + params: PlaidSubmitPublicTokenParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("plaidLinkToken", params.plaidLinkToken()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("plaid", "callback", params._pathParam(0)) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { submitPublicTokenHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformService.kt new file mode 100644 index 00000000..538e570f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformService.kt @@ -0,0 +1,75 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.platform.PlatformListInternalAccountsParams +import com.grid.api.models.platform.PlatformListInternalAccountsResponse +import com.grid.api.services.blocking.platform.ExternalAccountService + +interface PlatformService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlatformService + + fun externalAccounts(): ExternalAccountService + + /** + * Retrieve a list of all internal accounts that belong to the platform, as opposed to an + * individual customer. + * + * These accounts are created automatically when the platform is configured for each supported + * currency. They can be used for things like distributing bitcoin rewards to customers, or for + * other platform-wide purposes. + */ + fun listInternalAccounts( + params: PlatformListInternalAccountsParams = PlatformListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): PlatformListInternalAccountsResponse + + /** @see listInternalAccounts */ + fun listInternalAccounts(requestOptions: RequestOptions): PlatformListInternalAccountsResponse = + listInternalAccounts(PlatformListInternalAccountsParams.none(), requestOptions) + + /** A view of [PlatformService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlatformService.WithRawResponse + + fun externalAccounts(): ExternalAccountService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /platform/internal-accounts`, but is otherwise the + * same as [PlatformService.listInternalAccounts]. + */ + @MustBeClosed + fun listInternalAccounts( + params: PlatformListInternalAccountsParams = PlatformListInternalAccountsParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see listInternalAccounts */ + @MustBeClosed + fun listInternalAccounts( + requestOptions: RequestOptions + ): HttpResponseFor = + listInternalAccounts(PlatformListInternalAccountsParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformServiceImpl.kt new file mode 100644 index 00000000..665dfbad --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/PlatformServiceImpl.kt @@ -0,0 +1,93 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.platform.PlatformListInternalAccountsParams +import com.grid.api.models.platform.PlatformListInternalAccountsResponse +import com.grid.api.services.blocking.platform.ExternalAccountService +import com.grid.api.services.blocking.platform.ExternalAccountServiceImpl + +class PlatformServiceImpl internal constructor(private val clientOptions: ClientOptions) : + PlatformService { + + private val withRawResponse: PlatformService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val externalAccounts: ExternalAccountService by lazy { + ExternalAccountServiceImpl(clientOptions) + } + + override fun withRawResponse(): PlatformService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PlatformService = + PlatformServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun externalAccounts(): ExternalAccountService = externalAccounts + + override fun listInternalAccounts( + params: PlatformListInternalAccountsParams, + requestOptions: RequestOptions, + ): PlatformListInternalAccountsResponse = + // get /platform/internal-accounts + withRawResponse().listInternalAccounts(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + PlatformService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val externalAccounts: ExternalAccountService.WithRawResponse by lazy { + ExternalAccountServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): PlatformService.WithRawResponse = + PlatformServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun externalAccounts(): ExternalAccountService.WithRawResponse = externalAccounts + + private val listInternalAccountsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun listInternalAccounts( + params: PlatformListInternalAccountsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("platform", "internal-accounts") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listInternalAccountsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteService.kt new file mode 100644 index 00000000..c5a55c2f --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteService.kt @@ -0,0 +1,209 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteExecuteParams +import com.grid.api.models.quotes.QuoteListPage +import com.grid.api.models.quotes.QuoteListParams +import com.grid.api.models.quotes.QuoteRetrieveParams + +interface QuoteService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): QuoteService + + /** + * Generate a quote for a cross-currency transfer between any combination of accounts and UMA + * addresses. This endpoint handles currency exchange and provides the necessary instructions to + * execute the transfer. + * + * **Transfer Types Supported:** + * - **Account to Account**: Transfer between internal/external accounts with currency exchange. + * - **Account to UMA**: Transfer from an internal account to an UMA address. + * - **UMA to Account or UMA to UMA**: This transfer type will only be funded by payment + * instructions, not from an internal account. + * + * **Key Features:** + * - **Flexible Amount Locking**: Always specify whether you want to lock the sending amount or + * receiving amount + * - **Currency Exchange**: Handles all cross-currency transfers with real-time exchange rates + * - **Payment Instructions**: For UMA or customer ID sources, provides banking details needed + * for execution + * + * **Important:** If you are transferring funds in the same currency (no exchange required), use + * the `/transfer-in` or `/transfer-out` endpoints instead. + * + * **Sandbox Testing:** When using the `externalAccountDetails` destination type in sandbox + * mode, use account number patterns ending in specific digits to test different scenarios. + * These patterns should be used with the primary alias, address, or identifier of whatever + * account type you're testing. For example, the US account number, a CLABE, an IBAN, a spark + * wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ + fun create( + params: QuoteCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote + + /** + * Retrieve a quote by its ID. If the quote has been settled, it will include the transaction + * ID. This allows clients to track the full lifecycle of a payment from quote creation to + * settlement. + */ + fun retrieve( + quoteId: String, + params: QuoteRetrieveParams = QuoteRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote = retrieve(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote + + /** @see retrieve */ + fun retrieve(quoteId: String, requestOptions: RequestOptions): Quote = + retrieve(quoteId, QuoteRetrieveParams.none(), requestOptions) + + /** + * Retrieve a list of transfer quotes with optional filtering parameters. Returns all quotes + * that match the specified filters. If no filters are provided, returns all quotes (paginated). + */ + fun list( + params: QuoteListParams = QuoteListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): QuoteListPage + + /** @see list */ + fun list(requestOptions: RequestOptions): QuoteListPage = + list(QuoteListParams.none(), requestOptions) + + /** + * Execute a quote by its ID. This endpoint initiates the transfer between the source and + * destination accounts. + * + * This endpoint can only be used for quotes with a `source` which is either an internal + * account, or has direct pull functionality (e.g. ACH pull with an external account). + * + * Once executed, the quote cannot be cancelled and the transfer will be processed. + */ + fun execute( + quoteId: String, + params: QuoteExecuteParams = QuoteExecuteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote = execute(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see execute */ + fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Quote + + /** @see execute */ + fun execute(quoteId: String, requestOptions: RequestOptions): Quote = + execute(quoteId, QuoteExecuteParams.none(), requestOptions) + + /** A view of [QuoteService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): QuoteService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /quotes`, but is otherwise the same as + * [QuoteService.create]. + */ + @MustBeClosed + fun create( + params: QuoteCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /quotes/{quoteId}`, but is otherwise the same as + * [QuoteService.retrieve]. + */ + @MustBeClosed + fun retrieve( + quoteId: String, + params: QuoteRetrieveParams = QuoteRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve(quoteId: String, requestOptions: RequestOptions): HttpResponseFor = + retrieve(quoteId, QuoteRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /quotes`, but is otherwise the same as + * [QuoteService.list]. + */ + @MustBeClosed + fun list( + params: QuoteListParams = QuoteListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(QuoteListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /quotes/{quoteId}/execute`, but is otherwise the + * same as [QuoteService.execute]. + */ + @MustBeClosed + fun execute( + quoteId: String, + params: QuoteExecuteParams = QuoteExecuteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + execute(params.toBuilder().quoteId(quoteId).build(), requestOptions) + + /** @see execute */ + @MustBeClosed + fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see execute */ + @MustBeClosed + fun execute(quoteId: String, requestOptions: RequestOptions): HttpResponseFor = + execute(quoteId, QuoteExecuteParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteServiceImpl.kt new file mode 100644 index 00000000..cafd6c19 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/QuoteServiceImpl.kt @@ -0,0 +1,186 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.quotes.Quote +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteExecuteParams +import com.grid.api.models.quotes.QuoteListPage +import com.grid.api.models.quotes.QuoteListPageResponse +import com.grid.api.models.quotes.QuoteListParams +import com.grid.api.models.quotes.QuoteRetrieveParams + +class QuoteServiceImpl internal constructor(private val clientOptions: ClientOptions) : + QuoteService { + + private val withRawResponse: QuoteService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): QuoteService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): QuoteService = + QuoteServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create(params: QuoteCreateParams, requestOptions: RequestOptions): Quote = + // post /quotes + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve(params: QuoteRetrieveParams, requestOptions: RequestOptions): Quote = + // get /quotes/{quoteId} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list(params: QuoteListParams, requestOptions: RequestOptions): QuoteListPage = + // get /quotes + withRawResponse().list(params, requestOptions).parse() + + override fun execute(params: QuoteExecuteParams, requestOptions: RequestOptions): Quote = + // post /quotes/{quoteId}/execute + withRawResponse().execute(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + QuoteService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): QuoteService.WithRawResponse = + QuoteServiceImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + private val createHandler: Handler = jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: QuoteCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: QuoteRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("quoteId", params.quoteId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: QuoteListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + QuoteListPage.builder() + .service(QuoteServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val executeHandler: Handler = jsonHandler(clientOptions.jsonMapper) + + override fun execute( + params: QuoteExecuteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("quoteId", params.quoteId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("quotes", params._pathParam(0), "execute") + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { executeHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverService.kt new file mode 100644 index 00000000..ca748ebb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverService.kt @@ -0,0 +1,150 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.receiver.ReceiverLookupExternalAccountParams +import com.grid.api.models.receiver.ReceiverLookupExternalAccountResponse +import com.grid.api.models.receiver.ReceiverLookupUmaParams +import com.grid.api.models.receiver.ReceiverLookupUmaResponse + +interface ReceiverService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ReceiverService + + /** + * Lookup an external account by ID to determine supported currencies and exchange rates. This + * endpoint helps platforms determine what currencies they can send to a given external account, + * along with the current estimated exchange rates and minimum and maximum amounts that can be + * sent. + */ + fun lookupExternalAccount( + accountId: String, + params: ReceiverLookupExternalAccountParams = ReceiverLookupExternalAccountParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupExternalAccountResponse = + lookupExternalAccount(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see lookupExternalAccount */ + fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupExternalAccountResponse + + /** @see lookupExternalAccount */ + fun lookupExternalAccount( + accountId: String, + requestOptions: RequestOptions, + ): ReceiverLookupExternalAccountResponse = + lookupExternalAccount(accountId, ReceiverLookupExternalAccountParams.none(), requestOptions) + + /** + * Lookup a receiving UMA address to determine supported currencies and exchange rates. This + * endpoint helps platforms determine what currencies they can send to a given UMA address. + */ + fun lookupUma( + receiverUmaAddress: String, + params: ReceiverLookupUmaParams = ReceiverLookupUmaParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupUmaResponse = + lookupUma(params.toBuilder().receiverUmaAddress(receiverUmaAddress).build(), requestOptions) + + /** @see lookupUma */ + fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ReceiverLookupUmaResponse + + /** @see lookupUma */ + fun lookupUma( + receiverUmaAddress: String, + requestOptions: RequestOptions, + ): ReceiverLookupUmaResponse = + lookupUma(receiverUmaAddress, ReceiverLookupUmaParams.none(), requestOptions) + + /** A view of [ReceiverService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ReceiverService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /receiver/external-account/{accountId}`, but is + * otherwise the same as [ReceiverService.lookupExternalAccount]. + */ + @MustBeClosed + fun lookupExternalAccount( + accountId: String, + params: ReceiverLookupExternalAccountParams = + ReceiverLookupExternalAccountParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + lookupExternalAccount(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see lookupExternalAccount */ + @MustBeClosed + fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see lookupExternalAccount */ + @MustBeClosed + fun lookupExternalAccount( + accountId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + lookupExternalAccount( + accountId, + ReceiverLookupExternalAccountParams.none(), + requestOptions, + ) + + /** + * Returns a raw HTTP response for `get /receiver/uma/{receiverUmaAddress}`, but is + * otherwise the same as [ReceiverService.lookupUma]. + */ + @MustBeClosed + fun lookupUma( + receiverUmaAddress: String, + params: ReceiverLookupUmaParams = ReceiverLookupUmaParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + lookupUma( + params.toBuilder().receiverUmaAddress(receiverUmaAddress).build(), + requestOptions, + ) + + /** @see lookupUma */ + @MustBeClosed + fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see lookupUma */ + @MustBeClosed + fun lookupUma( + receiverUmaAddress: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + lookupUma(receiverUmaAddress, ReceiverLookupUmaParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverServiceImpl.kt new file mode 100644 index 00000000..486d29fc --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/ReceiverServiceImpl.kt @@ -0,0 +1,122 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.receiver.ReceiverLookupExternalAccountParams +import com.grid.api.models.receiver.ReceiverLookupExternalAccountResponse +import com.grid.api.models.receiver.ReceiverLookupUmaParams +import com.grid.api.models.receiver.ReceiverLookupUmaResponse + +class ReceiverServiceImpl internal constructor(private val clientOptions: ClientOptions) : + ReceiverService { + + private val withRawResponse: ReceiverService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ReceiverService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ReceiverService = + ReceiverServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions, + ): ReceiverLookupExternalAccountResponse = + // get /receiver/external-account/{accountId} + withRawResponse().lookupExternalAccount(params, requestOptions).parse() + + override fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions, + ): ReceiverLookupUmaResponse = + // get /receiver/uma/{receiverUmaAddress} + withRawResponse().lookupUma(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ReceiverService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ReceiverService.WithRawResponse = + ReceiverServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val lookupExternalAccountHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun lookupExternalAccount( + params: ReceiverLookupExternalAccountParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("accountId", params.accountId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("receiver", "external-account", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { lookupExternalAccountHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val lookupUmaHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun lookupUma( + params: ReceiverLookupUmaParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("receiverUmaAddress", params.receiverUmaAddress()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("receiver", "uma", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { lookupUmaHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxService.kt new file mode 100644 index 00000000..25b28be8 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxService.kt @@ -0,0 +1,65 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.sandbox.SandboxSendFundsParams +import com.grid.api.models.sandbox.SandboxSendFundsResponse +import com.grid.api.services.blocking.sandbox.InternalAccountService +import com.grid.api.services.blocking.sandbox.UmaService + +interface SandboxService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): SandboxService + + fun uma(): UmaService + + fun internalAccounts(): InternalAccountService + + /** + * Simulate sending funds to the bank account as instructed in the quote. This endpoint is only + * for the sandbox environment and will fail for production platforms/keys. + */ + fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): SandboxSendFundsResponse + + /** A view of [SandboxService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): SandboxService.WithRawResponse + + fun uma(): UmaService.WithRawResponse + + fun internalAccounts(): InternalAccountService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /sandbox/send`, but is otherwise the same as + * [SandboxService.sendFunds]. + */ + @MustBeClosed + fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxServiceImpl.kt new file mode 100644 index 00000000..e105fedb --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/SandboxServiceImpl.kt @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.sandbox.SandboxSendFundsParams +import com.grid.api.models.sandbox.SandboxSendFundsResponse +import com.grid.api.services.blocking.sandbox.InternalAccountService +import com.grid.api.services.blocking.sandbox.InternalAccountServiceImpl +import com.grid.api.services.blocking.sandbox.UmaService +import com.grid.api.services.blocking.sandbox.UmaServiceImpl + +class SandboxServiceImpl internal constructor(private val clientOptions: ClientOptions) : + SandboxService { + + private val withRawResponse: SandboxService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val uma: UmaService by lazy { UmaServiceImpl(clientOptions) } + + private val internalAccounts: InternalAccountService by lazy { + InternalAccountServiceImpl(clientOptions) + } + + override fun withRawResponse(): SandboxService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): SandboxService = + SandboxServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun uma(): UmaService = uma + + override fun internalAccounts(): InternalAccountService = internalAccounts + + override fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions, + ): SandboxSendFundsResponse = + // post /sandbox/send + withRawResponse().sendFunds(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + SandboxService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val uma: UmaService.WithRawResponse by lazy { + UmaServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val internalAccounts: InternalAccountService.WithRawResponse by lazy { + InternalAccountServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): SandboxService.WithRawResponse = + SandboxServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + override fun uma(): UmaService.WithRawResponse = uma + + override fun internalAccounts(): InternalAccountService.WithRawResponse = internalAccounts + + private val sendFundsHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun sendFunds( + params: SandboxSendFundsParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("sandbox", "send") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { sendFundsHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenService.kt new file mode 100644 index 00000000..8246e123 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenService.kt @@ -0,0 +1,163 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.tokens.ApiToken +import com.grid.api.models.tokens.TokenCreateParams +import com.grid.api.models.tokens.TokenDeleteParams +import com.grid.api.models.tokens.TokenListPage +import com.grid.api.models.tokens.TokenListParams +import com.grid.api.models.tokens.TokenRetrieveParams + +interface TokenService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TokenService + + /** Create a new API token to access the Grid APIs. */ + fun create( + params: TokenCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiToken + + /** Retrieve an API token by their system-generated ID */ + fun retrieve( + tokenId: String, + params: TokenRetrieveParams = TokenRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiToken = retrieve(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiToken + + /** @see retrieve */ + fun retrieve(tokenId: String, requestOptions: RequestOptions): ApiToken = + retrieve(tokenId, TokenRetrieveParams.none(), requestOptions) + + /** + * Retrieve a list of API tokens with optional filtering parameters. Returns all tokens that + * match the specified filters. If no filters are provided, returns all tokens (paginated). + */ + fun list( + params: TokenListParams = TokenListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): TokenListPage + + /** @see list */ + fun list(requestOptions: RequestOptions): TokenListPage = + list(TokenListParams.none(), requestOptions) + + /** Delete an API token by their system-generated ID */ + fun delete( + tokenId: String, + params: TokenDeleteParams = TokenDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ) = delete(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see delete */ + fun delete(params: TokenDeleteParams, requestOptions: RequestOptions = RequestOptions.none()) + + /** @see delete */ + fun delete(tokenId: String, requestOptions: RequestOptions) = + delete(tokenId, TokenDeleteParams.none(), requestOptions) + + /** A view of [TokenService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TokenService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /tokens`, but is otherwise the same as + * [TokenService.create]. + */ + @MustBeClosed + fun create( + params: TokenCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /tokens/{tokenId}`, but is otherwise the same as + * [TokenService.retrieve]. + */ + @MustBeClosed + fun retrieve( + tokenId: String, + params: TokenRetrieveParams = TokenRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve(tokenId: String, requestOptions: RequestOptions): HttpResponseFor = + retrieve(tokenId, TokenRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /tokens`, but is otherwise the same as + * [TokenService.list]. + */ + @MustBeClosed + fun list( + params: TokenListParams = TokenListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(TokenListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /tokens/{tokenId}`, but is otherwise the same as + * [TokenService.delete]. + */ + @MustBeClosed + fun delete( + tokenId: String, + params: TokenDeleteParams = TokenDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = delete(params.toBuilder().tokenId(tokenId).build(), requestOptions) + + /** @see delete */ + @MustBeClosed + fun delete( + params: TokenDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + /** @see delete */ + @MustBeClosed + fun delete(tokenId: String, requestOptions: RequestOptions): HttpResponse = + delete(tokenId, TokenDeleteParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenServiceImpl.kt new file mode 100644 index 00000000..f31a0086 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TokenServiceImpl.kt @@ -0,0 +1,184 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.emptyHandler +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.tokens.ApiToken +import com.grid.api.models.tokens.TokenCreateParams +import com.grid.api.models.tokens.TokenDeleteParams +import com.grid.api.models.tokens.TokenListPage +import com.grid.api.models.tokens.TokenListPageResponse +import com.grid.api.models.tokens.TokenListParams +import com.grid.api.models.tokens.TokenRetrieveParams + +class TokenServiceImpl internal constructor(private val clientOptions: ClientOptions) : + TokenService { + + private val withRawResponse: TokenService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TokenService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TokenService = + TokenServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create(params: TokenCreateParams, requestOptions: RequestOptions): ApiToken = + // post /tokens + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve(params: TokenRetrieveParams, requestOptions: RequestOptions): ApiToken = + // get /tokens/{tokenId} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list(params: TokenListParams, requestOptions: RequestOptions): TokenListPage = + // get /tokens + withRawResponse().list(params, requestOptions).parse() + + override fun delete(params: TokenDeleteParams, requestOptions: RequestOptions) { + // delete /tokens/{tokenId} + withRawResponse().delete(params, requestOptions) + } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TokenService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TokenService.WithRawResponse = + TokenServiceImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: TokenCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: TokenRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("tokenId", params.tokenId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: TokenListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + TokenListPage.builder() + .service(TokenServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val deleteHandler: Handler = emptyHandler() + + override fun delete( + params: TokenDeleteParams, + requestOptions: RequestOptions, + ): HttpResponse { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("tokenId", params.tokenId()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("tokens", params._pathParam(0)) + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response.use { deleteHandler.handle(it) } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionService.kt new file mode 100644 index 00000000..0a441c6a --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionService.kt @@ -0,0 +1,215 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.transactions.IncomingTransaction +import com.grid.api.models.transactions.TransactionApproveParams +import com.grid.api.models.transactions.TransactionListPage +import com.grid.api.models.transactions.TransactionListParams +import com.grid.api.models.transactions.TransactionRejectParams +import com.grid.api.models.transactions.TransactionRetrieveParams +import com.grid.api.models.transferin.Transaction + +interface TransactionService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransactionService + + /** Retrieve detailed information about a specific transaction */ + fun retrieve( + transactionId: String, + params: TransactionRetrieveParams = TransactionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction = + retrieve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction + + /** @see retrieve */ + fun retrieve(transactionId: String, requestOptions: RequestOptions): Transaction = + retrieve(transactionId, TransactionRetrieveParams.none(), requestOptions) + + /** + * Retrieve a paginated list of transactions with optional filtering. The transactions can be + * filtered by customer ID, platform customer ID, UMA address, date range, status, and + * transaction type. + */ + fun list( + params: TransactionListParams = TransactionListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): TransactionListPage + + /** @see list */ + fun list(requestOptions: RequestOptions): TransactionListPage = + list(TransactionListParams.none(), requestOptions) + + /** + * Approve a pending incoming payment that was previously acknowledged with a 202 response. This + * endpoint allows platforms to asynchronously approve payments after async processing. + */ + fun approve( + transactionId: String, + params: TransactionApproveParams = TransactionApproveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction = + approve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see approve */ + fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction + + /** @see approve */ + fun approve(transactionId: String, requestOptions: RequestOptions): IncomingTransaction = + approve(transactionId, TransactionApproveParams.none(), requestOptions) + + /** + * Reject a pending incoming payment that was previously acknowledged with a 202 response. This + * endpoint allows platforms to asynchronously reject payments after additional processing. + */ + fun reject( + transactionId: String, + params: TransactionRejectParams = TransactionRejectParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction = + reject(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see reject */ + fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction + + /** @see reject */ + fun reject(transactionId: String, requestOptions: RequestOptions): IncomingTransaction = + reject(transactionId, TransactionRejectParams.none(), requestOptions) + + /** + * A view of [TransactionService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransactionService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /transactions/{transactionId}`, but is otherwise the + * same as [TransactionService.retrieve]. + */ + @MustBeClosed + fun retrieve( + transactionId: String, + params: TransactionRetrieveParams = TransactionRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + transactionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(transactionId, TransactionRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /transactions`, but is otherwise the same as + * [TransactionService.list]. + */ + @MustBeClosed + fun list( + params: TransactionListParams = TransactionListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(TransactionListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /transactions/{transactionId}/approve`, but is + * otherwise the same as [TransactionService.approve]. + */ + @MustBeClosed + fun approve( + transactionId: String, + params: TransactionApproveParams = TransactionApproveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + approve(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see approve */ + @MustBeClosed + fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see approve */ + @MustBeClosed + fun approve( + transactionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + approve(transactionId, TransactionApproveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /transactions/{transactionId}/reject`, but is + * otherwise the same as [TransactionService.reject]. + */ + @MustBeClosed + fun reject( + transactionId: String, + params: TransactionRejectParams = TransactionRejectParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + reject(params.toBuilder().transactionId(transactionId).build(), requestOptions) + + /** @see reject */ + @MustBeClosed + fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see reject */ + @MustBeClosed + fun reject( + transactionId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + reject(transactionId, TransactionRejectParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionServiceImpl.kt new file mode 100644 index 00000000..99e09dc5 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransactionServiceImpl.kt @@ -0,0 +1,207 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.transactions.IncomingTransaction +import com.grid.api.models.transactions.TransactionApproveParams +import com.grid.api.models.transactions.TransactionListPage +import com.grid.api.models.transactions.TransactionListPageResponse +import com.grid.api.models.transactions.TransactionListParams +import com.grid.api.models.transactions.TransactionRejectParams +import com.grid.api.models.transactions.TransactionRetrieveParams +import com.grid.api.models.transferin.Transaction + +class TransactionServiceImpl internal constructor(private val clientOptions: ClientOptions) : + TransactionService { + + private val withRawResponse: TransactionService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TransactionService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransactionService = + TransactionServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions, + ): Transaction = + // get /transactions/{transactionId} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list( + params: TransactionListParams, + requestOptions: RequestOptions, + ): TransactionListPage = + // get /transactions + withRawResponse().list(params, requestOptions).parse() + + override fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions, + ): IncomingTransaction = + // post /transactions/{transactionId}/approve + withRawResponse().approve(params, requestOptions).parse() + + override fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions, + ): IncomingTransaction = + // post /transactions/{transactionId}/reject + withRawResponse().reject(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TransactionService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransactionService.WithRawResponse = + TransactionServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: TransactionRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("transactionId", params.transactionId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: TransactionListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + TransactionListPage.builder() + .service(TransactionServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + + private val approveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun approve( + params: TransactionApproveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("transactionId", params.transactionId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions", params._pathParam(0), "approve") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { approveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val rejectHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun reject( + params: TransactionRejectParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("transactionId", params.transactionId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transactions", params._pathParam(0), "reject") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { rejectHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInService.kt new file mode 100644 index 00000000..fd3a669c --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInService.kt @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferin.TransferInCreateParams + +interface TransferInService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferInService + + /** + * Transfer funds from an external account to an internal account for a specific customer. This + * endpoint should only be used for external account sources with pull functionality (e.g. ACH + * Pull). Otherwise, use the paymentInstructions on the internal account to deposit funds. + */ + fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction + + /** A view of [TransferInService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferInService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /transfer-in`, but is otherwise the same as + * [TransferInService.create]. + */ + @MustBeClosed + fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInServiceImpl.kt new file mode 100644 index 00000000..5aa47875 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferInServiceImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferin.TransferInCreateParams + +class TransferInServiceImpl internal constructor(private val clientOptions: ClientOptions) : + TransferInService { + + private val withRawResponse: TransferInService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TransferInService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferInService = + TransferInServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions, + ): Transaction = + // post /transfer-in + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TransferInService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferInService.WithRawResponse = + TransferInServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: TransferInCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transfer-in") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutService.kt new file mode 100644 index 00000000..7d71e2fd --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutService.kt @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferout.TransferOutCreateParams + +interface TransferOutService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferOutService + + /** Transfer funds from an internal account to an external account for a specific customer. */ + fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): Transaction + + /** + * A view of [TransferOutService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferOutService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /transfer-out`, but is otherwise the same as + * [TransferOutService.create]. + */ + @MustBeClosed + fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutServiceImpl.kt new file mode 100644 index 00000000..4ff47556 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/TransferOutServiceImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.transferin.Transaction +import com.grid.api.models.transferout.TransferOutCreateParams + +class TransferOutServiceImpl internal constructor(private val clientOptions: ClientOptions) : + TransferOutService { + + private val withRawResponse: TransferOutService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): TransferOutService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): TransferOutService = + TransferOutServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions, + ): Transaction = + // post /transfer-out + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + TransferOutService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): TransferOutService.WithRawResponse = + TransferOutServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: TransferOutCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("transfer-out") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderService.kt new file mode 100644 index 00000000..59c0788e --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderService.kt @@ -0,0 +1,71 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.umaproviders.UmaProviderListPage +import com.grid.api.models.umaproviders.UmaProviderListParams + +interface UmaProviderService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaProviderService + + /** + * This endpoint provides a list of Counterparty Providers that are available. + * + * The response includes basic information about each provider, such as its UMA address, name, + * and supported currencies. This can be used to determine which providers are available for + * sending or receiving payments. + */ + fun list( + params: UmaProviderListParams = UmaProviderListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UmaProviderListPage + + /** @see list */ + fun list(requestOptions: RequestOptions): UmaProviderListPage = + list(UmaProviderListParams.none(), requestOptions) + + /** + * A view of [UmaProviderService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): UmaProviderService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /uma-providers`, but is otherwise the same as + * [UmaProviderService.list]. + */ + @MustBeClosed + fun list( + params: UmaProviderListParams = UmaProviderListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(UmaProviderListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderServiceImpl.kt new file mode 100644 index 00000000..d7cd43a1 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/UmaProviderServiceImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.umaproviders.UmaProviderListPage +import com.grid.api.models.umaproviders.UmaProviderListPageResponse +import com.grid.api.models.umaproviders.UmaProviderListParams + +class UmaProviderServiceImpl internal constructor(private val clientOptions: ClientOptions) : + UmaProviderService { + + private val withRawResponse: UmaProviderService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): UmaProviderService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaProviderService = + UmaProviderServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun list( + params: UmaProviderListParams, + requestOptions: RequestOptions, + ): UmaProviderListPage = + // get /uma-providers + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + UmaProviderService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): UmaProviderService.WithRawResponse = + UmaProviderServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: UmaProviderListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("uma-providers") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + UmaProviderListPage.builder() + .service(UmaProviderServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookService.kt new file mode 100644 index 00000000..05ac1d96 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookService.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.webhooks.WebhookSendTestParams +import com.grid.api.models.webhooks.WebhookSendTestResponse + +interface WebhookService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): WebhookService + + /** Send a test webhook to the configured endpoint */ + fun sendTest( + params: WebhookSendTestParams = WebhookSendTestParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): WebhookSendTestResponse + + /** @see sendTest */ + fun sendTest(requestOptions: RequestOptions): WebhookSendTestResponse = + sendTest(WebhookSendTestParams.none(), requestOptions) + + /** A view of [WebhookService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): WebhookService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /webhooks/test`, but is otherwise the same as + * [WebhookService.sendTest]. + */ + @MustBeClosed + fun sendTest( + params: WebhookSendTestParams = WebhookSendTestParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see sendTest */ + @MustBeClosed + fun sendTest(requestOptions: RequestOptions): HttpResponseFor = + sendTest(WebhookSendTestParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookServiceImpl.kt new file mode 100644 index 00000000..c34b8766 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/WebhookServiceImpl.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.webhooks.WebhookSendTestParams +import com.grid.api.models.webhooks.WebhookSendTestResponse + +class WebhookServiceImpl internal constructor(private val clientOptions: ClientOptions) : + WebhookService { + + private val withRawResponse: WebhookService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): WebhookService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): WebhookService = + WebhookServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun sendTest( + params: WebhookSendTestParams, + requestOptions: RequestOptions, + ): WebhookSendTestResponse = + // post /webhooks/test + withRawResponse().sendTest(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + WebhookService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): WebhookService.WithRawResponse = + WebhookServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val sendTestHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun sendTest( + params: WebhookSendTestParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("webhooks", "test") + .apply { params._body()?.let { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { sendTestHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkService.kt new file mode 100644 index 00000000..93abd4b0 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkService.kt @@ -0,0 +1,159 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.customers + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.bulk.BulkGetJobStatusParams +import com.grid.api.models.customers.bulk.BulkGetJobStatusResponse +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse + +interface BulkService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): BulkService + + /** + * Retrieve the current status and results of a bulk customer import job. This endpoint can be + * used to track the progress of both CSV uploads. + * + * The response includes: + * - Overall job status + * - Progress statistics + * - Detailed error information for failed entries + * - Completion timestamp when finished + */ + fun getJobStatus( + jobId: String, + params: BulkGetJobStatusParams = BulkGetJobStatusParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): BulkGetJobStatusResponse = + getJobStatus(params.toBuilder().jobId(jobId).build(), requestOptions) + + /** @see getJobStatus */ + fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): BulkGetJobStatusResponse + + /** @see getJobStatus */ + fun getJobStatus(jobId: String, requestOptions: RequestOptions): BulkGetJobStatusResponse = + getJobStatus(jobId, BulkGetJobStatusParams.none(), requestOptions) + + /** + * Upload a CSV file containing customer information for bulk creation. The CSV file should + * follow a specific format with required and optional columns based on customer type. + * + * ### CSV Format + * The CSV file should have the following columns: + * + * Required columns for all customers: + * - umaAddress: The customer's UMA address (e.g., $john.doe@uma.domain.com) + * - platformCustomerId: Your platform's unique identifier for the customer + * - customerType: Either "INDIVIDUAL" or "BUSINESS" + * + * Required columns for individual customers: + * - fullName: Individual's full name + * - birthDate: Date of birth in YYYY-MM-DD format + * - addressLine1: Street address line 1 + * - city: City + * - state: State/Province/Region + * - postalCode: Postal/ZIP code + * - country: Country code (ISO 3166-1 alpha-2) + * + * Required columns for business customers: + * - businessLegalName: Legal name of the business + * - addressLine1: Street address line 1 + * - city: City + * - state: State/Province/Region + * - postalCode: Postal/ZIP code + * - country: Country code (ISO 3166-1 alpha-2) + * + * Optional columns for all customers: + * - addressLine2: Street address line 2 + * - platformAccountId: Your platform's identifier for the bank account + * - description: Optional description for the customer + * + * Optional columns for individual customers: + * - email: Customer's email address + * + * Optional columns for business customers: + * - businessRegistrationNumber: Business registration number + * - businessTaxId: Tax identification number + * + * ### Example CSV + * + * ```csv + * umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName + * john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US + * acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US + * ``` + * + * The upload process is asynchronous and will return a job ID that can be used to track + * progress. You can monitor the job status using the `/customers/bulk/jobs/{jobId}` endpoint. + */ + fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): BulkUploadCsvResponse + + /** A view of [BulkService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): BulkService.WithRawResponse + + /** + * Returns a raw HTTP response for `get /customers/bulk/jobs/{jobId}`, but is otherwise the + * same as [BulkService.getJobStatus]. + */ + @MustBeClosed + fun getJobStatus( + jobId: String, + params: BulkGetJobStatusParams = BulkGetJobStatusParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + getJobStatus(params.toBuilder().jobId(jobId).build(), requestOptions) + + /** @see getJobStatus */ + @MustBeClosed + fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see getJobStatus */ + @MustBeClosed + fun getJobStatus( + jobId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + getJobStatus(jobId, BulkGetJobStatusParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /customers/bulk/csv`, but is otherwise the same as + * [BulkService.uploadCsv]. + */ + @MustBeClosed + fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkServiceImpl.kt new file mode 100644 index 00000000..1f2fc5e9 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/BulkServiceImpl.kt @@ -0,0 +1,118 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.customers + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.multipartFormData +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.customers.bulk.BulkGetJobStatusParams +import com.grid.api.models.customers.bulk.BulkGetJobStatusResponse +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import com.grid.api.models.customers.bulk.BulkUploadCsvResponse + +class BulkServiceImpl internal constructor(private val clientOptions: ClientOptions) : BulkService { + + private val withRawResponse: BulkService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): BulkService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): BulkService = + BulkServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions, + ): BulkGetJobStatusResponse = + // get /customers/bulk/jobs/{jobId} + withRawResponse().getJobStatus(params, requestOptions).parse() + + override fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions, + ): BulkUploadCsvResponse = + // post /customers/bulk/csv + withRawResponse().uploadCsv(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + BulkService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): BulkService.WithRawResponse = + BulkServiceImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + private val getJobStatusHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun getJobStatus( + params: BulkGetJobStatusParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("jobId", params.jobId()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "bulk", "jobs", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { getJobStatusHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val uploadCsvHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun uploadCsv( + params: BulkUploadCsvParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "bulk", "csv") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { uploadCsvHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountService.kt new file mode 100644 index 00000000..4b8ec4da --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountService.kt @@ -0,0 +1,129 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.customers + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.customers.externalaccounts.ExternalAccountListPage +import com.grid.api.models.customers.externalaccounts.ExternalAccountListParams + +interface ExternalAccountService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExternalAccountService + + /** + * Register a new external bank account for a customer. + * + * **Sandbox Testing:** In sandbox mode, use these account number patterns to test different + * transfer scenarios. These patterns should be used with the primary alias, address, or + * identifier of whatever account type you're testing. For example, the US account number, a + * CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ + fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount + + /** @see create */ + fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Retrieve a list of external accounts with optional filtering parameters. Returns all external + * accounts that match the specified filters. If no filters are provided, returns all external + * accounts (paginated). + * + * External accounts are bank accounts, cryptocurrency wallets, or other payment destinations + * that customers can use to receive funds from the platform. + */ + fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccountListPage + + /** @see list */ + fun list(requestOptions: RequestOptions): ExternalAccountListPage = + list(ExternalAccountListParams.none(), requestOptions) + + /** + * A view of [ExternalAccountService] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /customers/external-accounts`, but is otherwise the + * same as [ExternalAccountService.create]. + */ + @MustBeClosed + fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Returns a raw HTTP response for `get /customers/external-accounts`, but is otherwise the + * same as [ExternalAccountService.list]. + */ + @MustBeClosed + fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(ExternalAccountListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceImpl.kt new file mode 100644 index 00000000..aecbe107 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceImpl.kt @@ -0,0 +1,125 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.customers + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.customers.externalaccounts.ExternalAccountListPage +import com.grid.api.models.customers.externalaccounts.ExternalAccountListPageResponse +import com.grid.api.models.customers.externalaccounts.ExternalAccountListParams + +class ExternalAccountServiceImpl internal constructor(private val clientOptions: ClientOptions) : + ExternalAccountService { + + private val withRawResponse: ExternalAccountService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ExternalAccountService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExternalAccountService = + ExternalAccountServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): ExternalAccount = + // post /customers/external-accounts + withRawResponse().create(params, requestOptions).parse() + + override fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): ExternalAccountListPage = + // get /customers/external-accounts + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ExternalAccountService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountService.WithRawResponse = + ExternalAccountServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "external-accounts") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("customers", "external-accounts") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + .let { + ExternalAccountListPage.builder() + .service(ExternalAccountServiceImpl(clientOptions)) + .params(params) + .response(it) + .build() + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountService.kt new file mode 100644 index 00000000..c9f9b14b --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountService.kt @@ -0,0 +1,128 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.platform + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.platform.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListResponse + +interface ExternalAccountService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExternalAccountService + + /** + * Register a new external bank account for the platform. + * + * **Sandbox Testing:** In sandbox mode, use these account number patterns to test different + * transfer scenarios. These patterns should be used with the primary alias, address, or + * identifier of whatever account type you're testing. For example, the US account number, a + * CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: + * - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) + * - Account numbers ending in **003**: Account closed/invalid (transfers will fail) + * - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) + * - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) + * - Any other account number: Success (transfers complete normally) + */ + fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount + + /** @see create */ + fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccount = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Retrieve a list of all external accounts that belong to the platform, as opposed to an + * individual customer. + * + * These accounts are used for platform-wide operations such as receiving funds from external + * sources or managing platform-level payment destinations. + */ + fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ExternalAccountListResponse + + /** @see list */ + fun list(requestOptions: RequestOptions): ExternalAccountListResponse = + list(ExternalAccountListParams.none(), requestOptions) + + /** + * A view of [ExternalAccountService] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /platform/external-accounts`, but is otherwise the + * same as [ExternalAccountService.create]. + */ + @MustBeClosed + fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + fun create( + externalAccountCreate: ExternalAccountCreate, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + create( + ExternalAccountCreateParams.builder() + .externalAccountCreate(externalAccountCreate) + .build(), + requestOptions, + ) + + /** + * Returns a raw HTTP response for `get /platform/external-accounts`, but is otherwise the + * same as [ExternalAccountService.list]. + */ + @MustBeClosed + fun list( + params: ExternalAccountListParams = ExternalAccountListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(ExternalAccountListParams.none(), requestOptions) + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceImpl.kt new file mode 100644 index 00000000..b0261727 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceImpl.kt @@ -0,0 +1,117 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.platform + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.platform.externalaccounts.ExternalAccountCreateParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListParams +import com.grid.api.models.platform.externalaccounts.ExternalAccountListResponse + +class ExternalAccountServiceImpl internal constructor(private val clientOptions: ClientOptions) : + ExternalAccountService { + + private val withRawResponse: ExternalAccountService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ExternalAccountService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): ExternalAccountService = + ExternalAccountServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): ExternalAccount = + // post /platform/external-accounts + withRawResponse().create(params, requestOptions).parse() + + override fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): ExternalAccountListResponse = + // get /platform/external-accounts + withRawResponse().list(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ExternalAccountService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): ExternalAccountService.WithRawResponse = + ExternalAccountServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: ExternalAccountCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("platform", "external-accounts") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: ExternalAccountListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("platform", "external-accounts") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountService.kt new file mode 100644 index 00000000..49c9ff32 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountService.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.sandbox + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import com.grid.api.models.sandbox.internalaccounts.InternalAccountFundParams + +interface InternalAccountService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InternalAccountService + + /** + * Simulate receiving funds into an internal account in the sandbox environment. This is useful + * for testing scenarios where you need to add funds to a customer's or platform's internal + * account without going through a real bank transfer or following payment instructions. This + * endpoint is only for the sandbox environment and will fail for production platforms/keys. + */ + fun fund( + accountId: String, + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InternalAccount = fund(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see fund */ + fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InternalAccount + + /** + * A view of [InternalAccountService] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InternalAccountService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /sandbox/internal-accounts/{accountId}/fund`, but + * is otherwise the same as [InternalAccountService.fund]. + */ + @MustBeClosed + fun fund( + accountId: String, + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + fund(params.toBuilder().accountId(accountId).build(), requestOptions) + + /** @see fund */ + @MustBeClosed + fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceImpl.kt new file mode 100644 index 00000000..a61e2f64 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceImpl.kt @@ -0,0 +1,85 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.sandbox + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.checkRequired +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import com.grid.api.models.sandbox.internalaccounts.InternalAccountFundParams + +class InternalAccountServiceImpl internal constructor(private val clientOptions: ClientOptions) : + InternalAccountService { + + private val withRawResponse: InternalAccountService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InternalAccountService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): InternalAccountService = + InternalAccountServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions, + ): InternalAccount = + // post /sandbox/internal-accounts/{accountId}/fund + withRawResponse().fund(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InternalAccountService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): InternalAccountService.WithRawResponse = + InternalAccountServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier).build() + ) + + private val fundHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun fund( + params: InternalAccountFundParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("accountId", params.accountId()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("sandbox", "internal-accounts", params._pathParam(0), "fund") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { fundHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaService.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaService.kt new file mode 100644 index 00000000..7af51ade --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaService.kt @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.sandbox + +import com.google.errorprone.annotations.MustBeClosed +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.models.sandbox.uma.UmaReceivePaymentParams +import com.grid.api.models.transactions.IncomingTransaction + +interface UmaService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaService + + /** + * Simulate sending payment from an sandbox uma address to a platform customer to test payment + * receive. This endpoint is only for the sandbox environment and will fail for production + * platforms/keys. + */ + fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): IncomingTransaction + + /** A view of [UmaService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /sandbox/uma/receive`, but is otherwise the same as + * [UmaService.receivePayment]. + */ + @MustBeClosed + fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceImpl.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceImpl.kt new file mode 100644 index 00000000..fe31b070 --- /dev/null +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceImpl.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.sandbox + +import com.grid.api.core.ClientOptions +import com.grid.api.core.RequestOptions +import com.grid.api.core.handlers.errorBodyHandler +import com.grid.api.core.handlers.errorHandler +import com.grid.api.core.handlers.jsonHandler +import com.grid.api.core.http.HttpMethod +import com.grid.api.core.http.HttpRequest +import com.grid.api.core.http.HttpResponse +import com.grid.api.core.http.HttpResponse.Handler +import com.grid.api.core.http.HttpResponseFor +import com.grid.api.core.http.json +import com.grid.api.core.http.parseable +import com.grid.api.core.prepare +import com.grid.api.models.sandbox.uma.UmaReceivePaymentParams +import com.grid.api.models.transactions.IncomingTransaction + +class UmaServiceImpl internal constructor(private val clientOptions: ClientOptions) : UmaService { + + private val withRawResponse: UmaService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): UmaService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: (ClientOptions.Builder) -> Unit): UmaService = + UmaServiceImpl(clientOptions.toBuilder().apply(modifier).build()) + + override fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions, + ): IncomingTransaction = + // post /sandbox/uma/receive + withRawResponse().receivePayment(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + UmaService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: (ClientOptions.Builder) -> Unit + ): UmaService.WithRawResponse = + UmaServiceImpl.WithRawResponseImpl(clientOptions.toBuilder().apply(modifier).build()) + + private val receivePaymentHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun receivePayment( + params: UmaReceivePaymentParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("sandbox", "uma", "receive") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { receivePaymentHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro b/grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro new file mode 100644 index 00000000..fedf435d --- /dev/null +++ b/grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro @@ -0,0 +1,32 @@ +# Jackson uses reflection and depends heavily on runtime attributes. +-keepattributes Exceptions,InnerClasses,Signature,Deprecated,*Annotation* + +# Jackson uses Kotlin reflection utilities, which themselves use reflection to access things. +-keep class kotlin.reflect.** { *; } +-keep class kotlin.Metadata { *; } + +# Jackson uses reflection to access enum members (e.g. via `java.lang.Class.getEnumConstants()`). +-keepclassmembers class com.fasterxml.jackson.** extends java.lang.Enum { + ; + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Jackson uses reflection to access annotation members. +-keepclassmembers @interface com.fasterxml.jackson.annotation.** { + *; +} + +# Jackson uses reified type information to serialize and deserialize our classes (via `TypeReference`). +-keep class com.fasterxml.jackson.core.type.TypeReference { *; } +-keep class * extends com.fasterxml.jackson.core.type.TypeReference { *; } + +# Jackson uses reflection to access our class serializers and deserializers. +-keep @com.fasterxml.jackson.databind.annotation.JsonSerialize class com.grid.api.** { *; } +-keep @com.fasterxml.jackson.databind.annotation.JsonDeserialize class com.grid.api.** { *; } + +# Jackson uses reflection to serialize and deserialize our classes based on their constructors and annotated members. +-keepclassmembers class com.grid.api.** { + (...); + @com.fasterxml.jackson.annotation.* *; +} \ No newline at end of file diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/TestServerExtension.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/TestServerExtension.kt new file mode 100644 index 00000000..f875e57c --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/TestServerExtension.kt @@ -0,0 +1,62 @@ +package com.grid.api + +import java.lang.RuntimeException +import java.net.URL +import org.junit.jupiter.api.extension.BeforeAllCallback +import org.junit.jupiter.api.extension.ConditionEvaluationResult +import org.junit.jupiter.api.extension.ExecutionCondition +import org.junit.jupiter.api.extension.ExtensionContext + +class TestServerExtension : BeforeAllCallback, ExecutionCondition { + + override fun beforeAll(context: ExtensionContext?) { + try { + URL(BASE_URL).openConnection().connect() + } catch (e: Exception) { + throw RuntimeException( + """ + The test suite will not run without a mock Prism server running against your OpenAPI spec. + + You can set the environment variable `SKIP_MOCK_TESTS` to `true` to skip running any tests + that require the mock server. + + To fix: + + 1. Install Prism (requires Node 16+): + + With npm: + $ npm install -g @stoplight/prism-cli + + With yarn: + $ yarn global add @stoplight/prism-cli + + 2. Run the mock server + + To run the server, pass in the path of your OpenAPI spec to the prism command: + $ prism mock path/to/your.openapi.yml + """ + .trimIndent(), + e, + ) + } + } + + override fun evaluateExecutionCondition(context: ExtensionContext): ConditionEvaluationResult { + return if (System.getenv(SKIP_TESTS_ENV).toBoolean()) { + ConditionEvaluationResult.disabled( + "Environment variable $SKIP_TESTS_ENV is set to true" + ) + } else { + ConditionEvaluationResult.enabled( + "Environment variable $SKIP_TESTS_ENV is not set to true" + ) + } + } + + companion object { + + val BASE_URL = System.getenv("TEST_API_BASE_URL") ?: "http://localhost:4010" + + const val SKIP_TESTS_ENV: String = "SKIP_MOCK_TESTS" + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerAsyncTest.kt new file mode 100644 index 00000000..6fcc768f --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerAsyncTest.kt @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class AutoPagerAsyncTest { + + private class PageAsyncImpl( + private val items: List, + private val nextPage: PageAsync? = null, + ) : PageAsync { + + override fun hasNextPage(): Boolean = nextPage != null + + override suspend fun nextPage(): PageAsync = nextPage!! + + override fun items(): List = items + } + + @Test + fun collect() { + val firstPage = + PageAsyncImpl( + listOf("chunk1", "chunk2"), + nextPage = PageAsyncImpl(listOf("chunk3", "chunk4")), + ) + + val autoPager = AutoPagerAsync.from(firstPage) + + assertThat(runBlocking { autoPager.toList() }) + .containsExactly("chunk1", "chunk2", "chunk3", "chunk4") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerTest.kt new file mode 100644 index 00000000..135b4f58 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/AutoPagerTest.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class AutoPagerTest { + + private class PageImpl( + private val items: List, + private val nextPage: Page? = null, + ) : Page { + + override fun hasNextPage(): Boolean = nextPage != null + + override fun nextPage(): Page = nextPage!! + + override fun items(): List = items + } + + @Test + fun iterator() { + val firstPage = + PageImpl(listOf("chunk1", "chunk2"), nextPage = PageImpl(listOf("chunk3", "chunk4"))) + + val autoPager = AutoPager.from(firstPage) + + assertThat(autoPager.asIterable()).containsExactly("chunk1", "chunk2", "chunk3", "chunk4") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ClientOptionsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ClientOptionsTest.kt new file mode 100644 index 00000000..c3805664 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ClientOptionsTest.kt @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.core + +import com.grid.api.core.http.HttpClient +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +@ExtendWith(MockitoExtension::class) +internal class ClientOptionsTest { + + private val httpClient = mock() + + @Test + fun toBuilder_whenOriginalClientOptionsGarbageCollected_doesNotCloseOriginalClient() { + var clientOptions = + ClientOptions.builder() + .httpClient(httpClient) + .username("My Username") + .password("My Password") + .build() + verify(httpClient, never()).close() + + // Overwrite the `clientOptions` variable so that the original `ClientOptions` is GC'd. + clientOptions = clientOptions.toBuilder().build() + System.gc() + Thread.sleep(100) + + verify(httpClient, never()).close() + // This exists so that `clientOptions` is still reachable. + assertThat(clientOptions).isEqualTo(clientOptions) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ObjectMappersTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ObjectMappersTest.kt new file mode 100644 index 00000000..8f3b81ba --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ObjectMappersTest.kt @@ -0,0 +1,117 @@ +package com.grid.api.core + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.exc.MismatchedInputException +import com.fasterxml.jackson.module.kotlin.readValue +import java.time.LocalDate +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset +import kotlin.reflect.KClass +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowable +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource +import org.junitpioneer.jupiter.cartesian.CartesianTest + +internal class ObjectMappersTest { + + internal class ClassWithBooleanFieldPrefixedWithIs(private val isActive: JsonField) { + + @JsonProperty("is_active") @ExcludeMissing fun _isActive() = isActive + } + + @Test + fun write_whenFieldPrefixedWithIs_keepsPrefix() { + val value = ClassWithBooleanFieldPrefixedWithIs(JsonField.of(true)) + + val json = jsonMapper().writeValueAsString(value) + + assertThat(json).isEqualTo("{\"is_active\":true}") + } + + internal class Class(@get:JsonProperty("field") @JsonProperty("field") val field: String) + + enum class ShapeTestCase(val value: Any, val kClass: KClass<*>) { + STRING("Hello World!", String::class), + BOOLEAN(true, Boolean::class), + FLOAT(3.14F, Float::class), + DOUBLE(3.14, Double::class), + INTEGER(42, Int::class), + LONG(42L, Long::class), + MAP(mapOf("property" to "value"), Map::class), + CLASS(Class("Hello World!"), Class::class), + LIST(listOf(1, 2, 3), List::class); + + companion object { + val VALID_CONVERSIONS = + listOf( + FLOAT to DOUBLE, + DOUBLE to FLOAT, + INTEGER to FLOAT, + INTEGER to DOUBLE, + INTEGER to LONG, + LONG to FLOAT, + LONG to DOUBLE, + LONG to INTEGER, + CLASS to MAP, + ) + } + } + + @CartesianTest + fun read(@CartesianTest.Enum shape1: ShapeTestCase, @CartesianTest.Enum shape2: ShapeTestCase) { + val jsonMapper = jsonMapper() + val json = jsonMapper.writeValueAsString(shape1.value) + + val e = catchThrowable { jsonMapper.readValue(json, shape2.kClass.java) } + + if (shape1 == shape2 || shape1 to shape2 in ShapeTestCase.VALID_CONVERSIONS) { + assertThat(e).isNull() + } else { + assertThat(e).isInstanceOf(MismatchedInputException::class.java) + } + } + + enum class LenientOffsetDateTimeTestCase( + val string: String, + val expectedOffsetDateTime: OffsetDateTime, + ) { + DATE( + "1998-04-21", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(0, 0), ZoneOffset.UTC), + ), + DATE_TIME( + "1998-04-21T04:00:00", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(4, 0), ZoneOffset.UTC), + ), + ZONED_DATE_TIME_1( + "1998-04-21T04:00:00+03:00", + expectedOffsetDateTime = + OffsetDateTime.of( + LocalDate.of(1998, 4, 21), + LocalTime.of(4, 0), + ZoneOffset.ofHours(3), + ), + ), + ZONED_DATE_TIME_2( + "1998-04-21T04:00:00Z", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(4, 0), ZoneOffset.UTC), + ), + } + + @ParameterizedTest + @EnumSource + fun readOffsetDateTime_lenient(testCase: LenientOffsetDateTimeTestCase) { + val jsonMapper = jsonMapper() + val json = jsonMapper.writeValueAsString(testCase.string) + + val offsetDateTime = jsonMapper().readValue(json) + + assertThat(offsetDateTime).isEqualTo(testCase.expectedOffsetDateTime) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/PhantomReachableTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/PhantomReachableTest.kt new file mode 100644 index 00000000..7b5e54d4 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/PhantomReachableTest.kt @@ -0,0 +1,27 @@ +package com.grid.api.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PhantomReachableTest { + + @Test + fun closeWhenPhantomReachable_whenObservedIsGarbageCollected_closesCloseable() { + var closed = false + val closeable = AutoCloseable { closed = true } + + closeWhenPhantomReachable( + // Pass an inline object for the object to observe so that it becomes immediately + // unreachable. + Any(), + closeable, + ) + + assertThat(closed).isFalse() + + System.gc() + Thread.sleep(100) + + assertThat(closed).isTrue() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/UtilsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/UtilsTest.kt new file mode 100644 index 00000000..9c317881 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/UtilsTest.kt @@ -0,0 +1,33 @@ +package com.grid.api.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UtilsTest { + @Test + fun contentDeepEquals() { + assertThat(42 contentEquals 42).isTrue() + assertThat(42 contentEquals "Hello World!").isFalse() + assertThat(byteArrayOf(1, 2, 3) contentEquals byteArrayOf(1, 2, 3)).isTrue() + assertThat(byteArrayOf(1, 2, 3) contentEquals byteArrayOf(1, 2, 4)).isFalse() + assertThat( + arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) contentEquals + arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) + ) + .isTrue() + assertThat( + arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) contentEquals + arrayOf(byteArrayOf(1), byteArrayOf(2, 3)) + ) + .isFalse() + } + + @Test + fun contentToString() { + assertThat((42).contentToString()).isEqualTo("42") + assertThat("Hello World!".contentToString()).isEqualTo("Hello World!") + assertThat(byteArrayOf(1, 2, 3).contentToString()).isEqualTo("[1, 2, 3]") + assertThat(arrayOf(byteArrayOf(1, 2), byteArrayOf(3)).contentToString()) + .isEqualTo("[[1, 2], [3]]") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ValuesTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ValuesTest.kt new file mode 100644 index 00000000..6f488c58 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/ValuesTest.kt @@ -0,0 +1,127 @@ +package com.grid.api.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class ValuesTest { + companion object { + private val NON_JSON = Any() + } + + enum class TestCase( + val value: JsonField<*>, + val expectedIsMissing: Boolean = false, + val expectedIsNull: Boolean = false, + val expectedAsKnown: Any? = null, + val expectedAsBoolean: Boolean? = null, + val expectedAsNumber: Number? = null, + val expectedAsString: String? = null, + val expectedAsArray: List? = null, + val expectedAsObject: Map? = null, + ) { + MISSING(JsonMissing.of(), expectedIsMissing = true), + NULL(JsonNull.of(), expectedIsNull = true), + KNOWN(KnownValue.of(NON_JSON), expectedAsKnown = NON_JSON), + KNOWN_BOOLEAN(KnownValue.of(true), expectedAsKnown = true, expectedAsBoolean = true), + BOOLEAN(JsonBoolean.of(true), expectedAsBoolean = true), + KNOWN_NUMBER(KnownValue.of(42), expectedAsKnown = 42, expectedAsNumber = 42), + NUMBER(JsonNumber.of(42), expectedAsNumber = 42), + KNOWN_STRING(KnownValue.of("hello"), expectedAsKnown = "hello", expectedAsString = "hello"), + STRING(JsonString.of("hello"), expectedAsString = "hello"), + KNOWN_ARRAY_NOT_ALL_JSON( + KnownValue.of(listOf("a", "b", NON_JSON)), + expectedAsKnown = listOf("a", "b", NON_JSON), + ), + KNOWN_ARRAY( + KnownValue.of(listOf("a", "b", "c")), + expectedAsKnown = listOf("a", "b", "c"), + expectedAsArray = listOf(JsonString.of("a"), JsonString.of("b"), JsonString.of("c")), + ), + ARRAY( + JsonArray.of(listOf(JsonString.of("a"), JsonString.of("b"), JsonString.of("c"))), + expectedAsArray = listOf(JsonString.of("a"), JsonString.of("b"), JsonString.of("c")), + ), + KNOWN_OBJECT_NOT_ALL_STRING_KEYS( + KnownValue.of(mapOf("a" to "b", 42 to "c")), + expectedAsKnown = mapOf("a" to "b", 42 to "c"), + ), + KNOWN_OBJECT_NOT_ALL_JSON( + KnownValue.of(mapOf("a" to "b", "b" to NON_JSON)), + expectedAsKnown = mapOf("a" to "b", "b" to NON_JSON), + ), + KNOWN_OBJECT( + KnownValue.of(mapOf("a" to "b", "b" to "c")), + expectedAsKnown = mapOf("a" to "b", "b" to "c"), + expectedAsObject = mapOf("a" to JsonString.of("b"), "b" to JsonString.of("c")), + ), + OBJECT( + JsonObject.of(mapOf("a" to JsonString.of("b"), "b" to JsonString.of("c"))), + expectedAsObject = mapOf("a" to JsonString.of("b"), "b" to JsonString.of("c")), + ), + } + + @ParameterizedTest + @EnumSource + fun isMissing(testCase: TestCase) { + val isMissing = testCase.value.isMissing() + + assertThat(isMissing).isEqualTo(testCase.expectedIsMissing) + } + + @ParameterizedTest + @EnumSource + fun isNull(testCase: TestCase) { + val isNull = testCase.value.isNull() + + assertThat(isNull).isEqualTo(testCase.expectedIsNull) + } + + @ParameterizedTest + @EnumSource + fun asKnown(testCase: TestCase) { + val known = testCase.value.asKnown() + + assertThat(known).isEqualTo(testCase.expectedAsKnown) + } + + @ParameterizedTest + @EnumSource + fun asBoolean(testCase: TestCase) { + val boolean = testCase.value.asBoolean() + + assertThat(boolean).isEqualTo(testCase.expectedAsBoolean) + } + + @ParameterizedTest + @EnumSource + fun asNumber(testCase: TestCase) { + val number = testCase.value.asNumber() + + assertThat(number).isEqualTo(testCase.expectedAsNumber) + } + + @ParameterizedTest + @EnumSource + fun asString(testCase: TestCase) { + val string = testCase.value.asString() + + assertThat(string).isEqualTo(testCase.expectedAsString) + } + + @ParameterizedTest + @EnumSource + fun asArray(testCase: TestCase) { + val array = testCase.value.asArray() + + assertThat(array).isEqualTo(testCase.expectedAsArray) + } + + @ParameterizedTest + @EnumSource + fun asObject(testCase: TestCase) { + val obj = testCase.value.asObject() + + assertThat(obj).isEqualTo(testCase.expectedAsObject) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HeadersTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HeadersTest.kt new file mode 100644 index 00000000..2112db46 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HeadersTest.kt @@ -0,0 +1,242 @@ +package com.grid.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class HeadersTest { + + enum class TestCase( + val headers: Headers, + val expectedMap: Map>, + val expectedSize: Int, + ) { + EMPTY(Headers.builder().build(), expectedMap = mapOf(), expectedSize = 0), + PUT_ONE( + Headers.builder().put("name", "value").build(), + expectedMap = mapOf("name" to listOf("value")), + expectedSize = 1, + ), + PUT_MULTIPLE( + Headers.builder().put("name", listOf("value1", "value2")).build(), + expectedMap = mapOf("name" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT( + Headers.builder().put("name1", "value").put("name2", "value").build(), + expectedMap = mapOf("name1" to listOf("value"), "name2" to listOf("value")), + expectedSize = 2, + ), + MULTIPLE_PUT_SAME_NAME( + Headers.builder().put("name", "value1").put("name", "value2").build(), + expectedMap = mapOf("name" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT_MULTIPLE( + Headers.builder() + .put("name", listOf("value1", "value2")) + .put("name", listOf("value1", "value2")) + .build(), + expectedMap = mapOf("name" to listOf("value1", "value2", "value1", "value2")), + expectedSize = 4, + ), + PUT_CASE_INSENSITIVE( + Headers.builder() + .put("name", "value1") + .put("NAME", "value2") + .put("nAmE", "value3") + .build(), + expectedMap = mapOf("name" to listOf("value1", "value2", "value3")), + expectedSize = 3, + ), + PUT_ALL_MAP( + Headers.builder() + .putAll( + mapOf( + "name1" to listOf("value1", "value2"), + "name2" to listOf("value1", "value2"), + ) + ) + .build(), + expectedMap = + mapOf("name1" to listOf("value1", "value2"), "name2" to listOf("value1", "value2")), + expectedSize = 4, + ), + PUT_ALL_HEADERS( + Headers.builder().putAll(Headers.builder().put("name", "value").build()).build(), + expectedMap = mapOf("name" to listOf("value")), + expectedSize = 1, + ), + PUT_ALL_CASE_INSENSITIVE( + Headers.builder() + .putAll( + mapOf( + "name" to listOf("value1"), + "NAME" to listOf("value2"), + "nAmE" to listOf("value3"), + ) + ) + .build(), + expectedMap = mapOf("name" to listOf("value1", "value2", "value3")), + expectedSize = 3, + ), + REMOVE_ABSENT( + Headers.builder().remove("name").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_ONE( + Headers.builder().put("name", "value").remove("name").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_MULTIPLE( + Headers.builder().put("name", listOf("value1", "value2")).remove("name").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_CASE_INSENSITIVE( + Headers.builder().put("name", listOf("value1", "value2")).remove("NAME").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_ALL( + Headers.builder() + .put("name1", "value") + .put("name3", "value") + .removeAll(setOf("name1", "name2", "name3")) + .build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_ALL_CASE_INSENSITIVE( + Headers.builder() + .put("name1", "value") + .put("name3", "value") + .removeAll(setOf("NAME1", "nAmE3")) + .build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + CLEAR( + Headers.builder().put("name1", "value").put("name2", "value").clear().build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REPLACE_ONE_ABSENT( + Headers.builder().replace("name", "value").build(), + expectedMap = mapOf("name" to listOf("value")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_ONE( + Headers.builder().put("name", "value1").replace("name", "value2").build(), + expectedMap = mapOf("name" to listOf("value2")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_MULTIPLE( + Headers.builder() + .put("name", listOf("value1", "value2")) + .replace("name", "value3") + .build(), + expectedMap = mapOf("name" to listOf("value3")), + expectedSize = 1, + ), + REPLACE_MULTIPLE_ABSENT( + Headers.builder().replace("name", listOf("value1", "value2")).build(), + expectedMap = mapOf("name" to listOf("value1", "value2")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_ONE( + Headers.builder() + .put("name", "value1") + .replace("name", listOf("value2", "value3")) + .build(), + expectedMap = mapOf("name" to listOf("value2", "value3")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_MULTIPLE( + Headers.builder() + .put("name", listOf("value1", "value2")) + .replace("name", listOf("value3", "value4")) + .build(), + expectedMap = mapOf("name" to listOf("value3", "value4")), + expectedSize = 2, + ), + REPLACE_CASE_INSENSITIVE( + Headers.builder() + .put("name", "value1") + .replace("NAME", listOf("value2", "value3")) + .build(), + expectedMap = mapOf("NAME" to listOf("value2", "value3")), + expectedSize = 2, + ), + REPLACE_ALL_MAP( + Headers.builder() + .put("name1", "value1") + .put("name2", "value1") + .put("name3", "value1") + .replaceAll(mapOf("name1" to listOf("value2"), "name3" to listOf("value2"))) + .build(), + expectedMap = + mapOf( + "name1" to listOf("value2"), + "name2" to listOf("value1"), + "name3" to listOf("value2"), + ), + expectedSize = 3, + ), + REPLACE_ALL_HEADERS( + Headers.builder() + .put("name1", "value1") + .put("name2", "value1") + .put("name3", "value1") + .replaceAll(Headers.builder().put("name1", "value2").put("name3", "value2").build()) + .build(), + expectedMap = + mapOf( + "name1" to listOf("value2"), + "name2" to listOf("value1"), + "name3" to listOf("value2"), + ), + expectedSize = 3, + ), + REPLACE_ALL_CASE_INSENSITIVE( + Headers.builder() + .put("name1", "value1") + .put("name2", "value1") + .replaceAll(mapOf("NAME1" to listOf("value2"), "nAmE2" to listOf("value2"))) + .build(), + expectedMap = mapOf("NAME1" to listOf("value2"), "nAmE2" to listOf("value2")), + expectedSize = 2, + ), + } + + @ParameterizedTest + @EnumSource + fun namesAndValues(testCase: TestCase) { + val map = mutableMapOf>() + val headers = testCase.headers + headers.names().forEach { name -> map[name] = headers.values(name) } + + assertThat(map).isEqualTo(testCase.expectedMap) + } + + @ParameterizedTest + @EnumSource + fun caseInsensitiveNames(testCase: TestCase) { + val headers = testCase.headers + + for (name in headers.names()) { + assertThat(headers.values(name)).isEqualTo(headers.values(name.lowercase())) + assertThat(headers.values(name)).isEqualTo(headers.values(name.uppercase())) + } + } + + @ParameterizedTest + @EnumSource + fun size(testCase: TestCase) { + val size = testCase.headers.size + + assertThat(size).isEqualTo(testCase.expectedSize) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HttpRequestTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HttpRequestTest.kt new file mode 100644 index 00000000..04e0b190 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/HttpRequestTest.kt @@ -0,0 +1,110 @@ +package com.grid.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class HttpRequestTest { + + enum class UrlTestCase(val request: HttpRequest, val expectedUrl: String) { + BASE_URL_ONLY( + HttpRequest.builder().method(HttpMethod.GET).baseUrl("https://api.example.com").build(), + expectedUrl = "https://api.example.com", + ), + BASE_URL_WITH_TRAILING_SLASH( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com/") + .build(), + expectedUrl = "https://api.example.com/", + ), + SINGLE_PATH_SEGMENT( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .build(), + expectedUrl = "https://api.example.com/users", + ), + MULTIPLE_PATH_SEGMENTS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegments("users", "123", "profile") + .build(), + expectedUrl = "https://api.example.com/users/123/profile", + ), + PATH_SEGMENT_WITH_SPECIAL_CHARS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("user name") + .build(), + expectedUrl = "https://api.example.com/user+name", + ), + SINGLE_QUERY_PARAM( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParam("limit", "10") + .build(), + expectedUrl = "https://api.example.com/users?limit=10", + ), + MULTIPLE_QUERY_PARAMS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParam("limit", "10") + .putQueryParam("offset", "20") + .build(), + expectedUrl = "https://api.example.com/users?limit=10&offset=20", + ), + QUERY_PARAM_WITH_SPECIAL_CHARS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("search") + .putQueryParam("q", "hello world") + .build(), + expectedUrl = "https://api.example.com/search?q=hello+world", + ), + MULTIPLE_VALUES_SAME_PARAM( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParams("tags", listOf("admin", "user")) + .build(), + expectedUrl = "https://api.example.com/users?tags=admin&tags=user", + ), + BASE_URL_WITH_TRAILING_SLASH_AND_PATH( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com/") + .addPathSegment("users") + .build(), + expectedUrl = "https://api.example.com/users", + ), + COMPLEX_URL( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl("https://api.example.com") + .addPathSegments("v1", "users", "123") + .putQueryParams("include", listOf("profile", "settings")) + .putQueryParam("format", "json") + .build(), + expectedUrl = + "https://api.example.com/v1/users/123?include=profile&include=settings&format=json", + ), + } + + @ParameterizedTest + @EnumSource + fun url(testCase: UrlTestCase) { + val actualUrl = testCase.request.url() + + assertThat(actualUrl).isEqualTo(testCase.expectedUrl) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/QueryParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/QueryParamsTest.kt new file mode 100644 index 00000000..30f36504 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/QueryParamsTest.kt @@ -0,0 +1,180 @@ +package com.grid.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class QueryParamsTest { + + enum class TestCase( + val queryParams: QueryParams, + val expectedMap: Map>, + val expectedSize: Int, + ) { + EMPTY(QueryParams.builder().build(), expectedMap = mapOf(), expectedSize = 0), + PUT_ONE( + QueryParams.builder().put("key", "value").build(), + expectedMap = mapOf("key" to listOf("value")), + expectedSize = 1, + ), + PUT_MULTIPLE( + QueryParams.builder().put("key", listOf("value1", "value2")).build(), + expectedMap = mapOf("key" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT( + QueryParams.builder().put("key1", "value").put("key2", "value").build(), + expectedMap = mapOf("key1" to listOf("value"), "key2" to listOf("value")), + expectedSize = 2, + ), + MULTIPLE_PUT_SAME_NAME( + QueryParams.builder().put("key", "value1").put("key", "value2").build(), + expectedMap = mapOf("key" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT_MULTIPLE( + QueryParams.builder() + .put("key", listOf("value1", "value2")) + .put("key", listOf("value1", "value2")) + .build(), + expectedMap = mapOf("key" to listOf("value1", "value2", "value1", "value2")), + expectedSize = 4, + ), + PUT_ALL_MAP( + QueryParams.builder() + .putAll( + mapOf( + "key1" to listOf("value1", "value2"), + "key2" to listOf("value1", "value2"), + ) + ) + .build(), + expectedMap = + mapOf("key1" to listOf("value1", "value2"), "key2" to listOf("value1", "value2")), + expectedSize = 4, + ), + PUT_ALL_HEADERS( + QueryParams.builder().putAll(QueryParams.builder().put("key", "value").build()).build(), + expectedMap = mapOf("key" to listOf("value")), + expectedSize = 1, + ), + REMOVE_ABSENT( + QueryParams.builder().remove("key").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_ONE( + QueryParams.builder().put("key", "value").remove("key").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_MULTIPLE( + QueryParams.builder().put("key", listOf("value1", "value2")).remove("key").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_ALL( + QueryParams.builder() + .put("key1", "value") + .put("key3", "value") + .removeAll(setOf("key1", "key2", "key3")) + .build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + CLEAR( + QueryParams.builder().put("key1", "value").put("key2", "value").clear().build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REPLACE_ONE_ABSENT( + QueryParams.builder().replace("key", "value").build(), + expectedMap = mapOf("key" to listOf("value")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_ONE( + QueryParams.builder().put("key", "value1").replace("key", "value2").build(), + expectedMap = mapOf("key" to listOf("value2")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_MULTIPLE( + QueryParams.builder() + .put("key", listOf("value1", "value2")) + .replace("key", "value3") + .build(), + expectedMap = mapOf("key" to listOf("value3")), + expectedSize = 1, + ), + REPLACE_MULTIPLE_ABSENT( + QueryParams.builder().replace("key", listOf("value1", "value2")).build(), + expectedMap = mapOf("key" to listOf("value1", "value2")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_ONE( + QueryParams.builder() + .put("key", "value1") + .replace("key", listOf("value2", "value3")) + .build(), + expectedMap = mapOf("key" to listOf("value2", "value3")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_MULTIPLE( + QueryParams.builder() + .put("key", listOf("value1", "value2")) + .replace("key", listOf("value3", "value4")) + .build(), + expectedMap = mapOf("key" to listOf("value3", "value4")), + expectedSize = 2, + ), + REPLACE_ALL_MAP( + QueryParams.builder() + .put("key1", "value1") + .put("key2", "value1") + .put("key3", "value1") + .replaceAll(mapOf("key1" to listOf("value2"), "key3" to listOf("value2"))) + .build(), + expectedMap = + mapOf( + "key1" to listOf("value2"), + "key2" to listOf("value1"), + "key3" to listOf("value2"), + ), + expectedSize = 3, + ), + REPLACE_ALL_HEADERS( + QueryParams.builder() + .put("key1", "value1") + .put("key2", "value1") + .put("key3", "value1") + .replaceAll( + QueryParams.builder().put("key1", "value2").put("key3", "value2").build() + ) + .build(), + expectedMap = + mapOf( + "key1" to listOf("value2"), + "key2" to listOf("value1"), + "key3" to listOf("value2"), + ), + expectedSize = 3, + ), + } + + @ParameterizedTest + @EnumSource + fun keysAndValues(testCase: TestCase) { + val map = mutableMapOf>() + val queryParams = testCase.queryParams + queryParams.keys().forEach { key -> map[key] = queryParams.values(key) } + + assertThat(map).isEqualTo(testCase.expectedMap) + } + + @ParameterizedTest + @EnumSource + fun size(testCase: TestCase) { + val size = testCase.queryParams.size + + assertThat(size).isEqualTo(testCase.expectedSize) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/RetryingHttpClientTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/RetryingHttpClientTest.kt new file mode 100644 index 00000000..12bc486a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/core/http/RetryingHttpClientTest.kt @@ -0,0 +1,349 @@ +package com.grid.api.core.http + +import com.github.tomakehurst.wiremock.client.WireMock.* +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.github.tomakehurst.wiremock.stubbing.Scenario +import com.grid.api.client.okhttp.OkHttpClient +import com.grid.api.core.RequestOptions +import com.grid.api.core.Sleeper +import com.grid.api.errors.GridRetryableException +import java.io.InputStream +import java.time.Duration +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.parallel.ResourceLock +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class RetryingHttpClientTest { + + private var openResponseCount = 0 + private lateinit var baseUrl: String + private lateinit var httpClient: HttpClient + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + baseUrl = wmRuntimeInfo.httpBaseUrl + val okHttpClient = OkHttpClient.builder().build() + httpClient = + object : HttpClient { + + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = trackClose(okHttpClient.execute(request, requestOptions)) + + override suspend fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = trackClose(okHttpClient.executeAsync(request, requestOptions)) + + override fun close() = okHttpClient.close() + + private fun trackClose(response: HttpResponse): HttpResponse { + openResponseCount++ + return object : HttpResponse { + + private var isClosed = false + + override fun statusCode(): Int = response.statusCode() + + override fun headers(): Headers = response.headers() + + override fun body(): InputStream = response.body() + + override fun close() { + response.close() + if (isClosed) { + return + } + openResponseCount-- + isClosed = true + } + } + } + } + resetAllScenarios() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) + val retryingClient = retryingHttpClientBuilder().build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withIdempotencyHeader(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .withHeader("X-Some-Header", matching("stainless-java-retry-.+")) + .willReturn(ok()) + ) + val retryingClient = + retryingHttpClientBuilder().maxRetries(2).idempotencyHeader("X-Some-Header").build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterHeader(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + // First we fail with a retry after header given as a date + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn( + serviceUnavailable().withHeader("Retry-After", "Wed, 21 Oct 2015 07:28:00 GMT") + ) + .willSetStateTo("RETRY_AFTER_DATE") + ) + stubFor( + post(urlPathEqualTo("/something")) + // Then we fail with a retry after header given as a delay + .inScenario("foo") + .whenScenarioStateIs("RETRY_AFTER_DATE") + .willReturn(serviceUnavailable().withHeader("Retry-After", "1.234")) + .willSetStateTo("RETRY_AFTER_DELAY") + ) + stubFor( + post(urlPathEqualTo("/something")) + // Then we return a success + .inScenario("foo") + .whenScenarioStateIs("RETRY_AFTER_DELAY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val retryingClient = retryingHttpClientBuilder().maxRetries(2).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("0")), + ) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("1")), + ) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("2")), + ) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withOverwrittenRetryCountHeader(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") // first we fail with a retry after header given as a date + .whenScenarioStateIs(Scenario.STARTED) + .willReturn( + serviceUnavailable().withHeader("Retry-After", "Wed, 21 Oct 2015 07:28:00 GMT") + ) + .willSetStateTo("RETRY_AFTER_DATE") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") // then we return a success + .whenScenarioStateIs("RETRY_AFTER_DATE") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val retryingClient = retryingHttpClientBuilder().maxRetries(2).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .putHeader("x-stainless-retry-count", "42") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify( + 2, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("42")), + ) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterMsHeader(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn(serviceUnavailable().withHeader("Retry-After-Ms", "10")) + .willSetStateTo("RETRY_AFTER_DELAY") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") // then we return a success + .whenScenarioStateIs("RETRY_AFTER_DELAY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val retryingClient = retryingHttpClientBuilder().maxRetries(1).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify(2, postRequestedFor(urlPathEqualTo("/something"))) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryableException(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) + + var callCount = 0 + val failingHttpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + callCount++ + if (callCount == 1) { + throw GridRetryableException("Simulated retryable failure") + } + return httpClient.execute(request, requestOptions) + } + + override suspend fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + callCount++ + if (callCount == 1) { + throw GridRetryableException("Simulated retryable failure") + } + return httpClient.executeAsync(request, requestOptions) + } + + override fun close() = httpClient.close() + } + + val retryingClient = + RetryingHttpClient.builder() + .httpClient(failingHttpClient) + .maxRetries(2) + .sleeper( + object : Sleeper { + + override fun sleep(duration: Duration) {} + + override suspend fun sleepAsync(duration: Duration) {} + + override fun close() {} + } + ) + .build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("1")), + ) + verify( + 0, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("0")), + ) + assertNoResponseLeaks() + } + + private fun retryingHttpClientBuilder() = + RetryingHttpClient.builder() + .httpClient(httpClient) + // Use a no-op `Sleeper` to make the test fast. + .sleeper( + object : Sleeper { + + override fun sleep(duration: Duration) {} + + override suspend fun sleepAsync(duration: Duration) {} + + override fun close() {} + } + ) + + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = + if (async) runBlocking { executeAsync(request) } else execute(request) + + // When retrying, all failed responses should be closed. Only the final returned response should + // be open. + private fun assertNoResponseLeaks() = assertThat(openResponseCount).isEqualTo(1) +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigRetrieveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigRetrieveParamsTest.kt new file mode 100644 index 00000000..00940575 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigRetrieveParamsTest.kt @@ -0,0 +1,13 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import org.junit.jupiter.api.Test + +internal class ConfigRetrieveParamsTest { + + @Test + fun create() { + ConfigRetrieveParams.builder().build() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigUpdateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigUpdateParamsTest.kt new file mode 100644 index 00000000..dc292aca --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/ConfigUpdateParamsTest.kt @@ -0,0 +1,136 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.grid.api.models.receiver.CounterpartyFieldDefinition +import com.grid.api.models.transactions.TransactionType +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ConfigUpdateParamsTest { + + @Test + fun create() { + ConfigUpdateParams.builder() + .addSupportedCurrency( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField(CustomerInfoFieldName.FULL_NAME) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + .umaDomain("mycompany.com") + .webhookEndpoint("https://api.mycompany.com/webhooks/uma") + .build() + } + + @Test + fun body() { + val params = + ConfigUpdateParams.builder() + .addSupportedCurrency( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.FULL_NAME + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + .umaDomain("mycompany.com") + .webhookEndpoint("https://api.mycompany.com/webhooks/uma") + .build() + + val body = params._body() + + assertThat(body.supportedCurrencies()) + .containsExactly( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField(CustomerInfoFieldName.FULL_NAME) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + assertThat(body.umaDomain()).isEqualTo("mycompany.com") + assertThat(body.webhookEndpoint()).isEqualTo("https://api.mycompany.com/webhooks/uma") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = ConfigUpdateParams.builder().build() + + val body = params._body() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformConfigTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformConfigTest.kt new file mode 100644 index 00000000..f73b9e63 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformConfigTest.kt @@ -0,0 +1,161 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.receiver.CounterpartyFieldDefinition +import com.grid.api.models.transactions.TransactionType +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlatformConfigTest { + + @Test + fun create() { + val platformConfig = + PlatformConfig.builder() + .id("PlatformConfig:019542f5-b3e7-1d02-0000-000000000003") + .createdAt(OffsetDateTime.parse("2025-06-15T12:30:45Z")) + .isRegulatedFinancialInstitution(false) + .proxyUmaSubdomain("platform") + .addSupportedCurrency( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.FULL_NAME + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + .umaDomain("platform.uma.domain") + .updatedAt(OffsetDateTime.parse("2025-06-15T12:30:45Z")) + .webhookEndpoint("https://api.mycompany.com/webhooks/uma") + .build() + + assertThat(platformConfig.id()) + .isEqualTo("PlatformConfig:019542f5-b3e7-1d02-0000-000000000003") + assertThat(platformConfig.createdAt()) + .isEqualTo(OffsetDateTime.parse("2025-06-15T12:30:45Z")) + assertThat(platformConfig.isRegulatedFinancialInstitution()).isEqualTo(false) + assertThat(platformConfig.proxyUmaSubdomain()).isEqualTo("platform") + assertThat(platformConfig.supportedCurrencies()) + .containsExactly( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField(CustomerInfoFieldName.FULL_NAME) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + assertThat(platformConfig.umaDomain()).isEqualTo("platform.uma.domain") + assertThat(platformConfig.updatedAt()) + .isEqualTo(OffsetDateTime.parse("2025-06-15T12:30:45Z")) + assertThat(platformConfig.webhookEndpoint()) + .isEqualTo("https://api.mycompany.com/webhooks/uma") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val platformConfig = + PlatformConfig.builder() + .id("PlatformConfig:019542f5-b3e7-1d02-0000-000000000003") + .createdAt(OffsetDateTime.parse("2025-06-15T12:30:45Z")) + .isRegulatedFinancialInstitution(false) + .proxyUmaSubdomain("platform") + .addSupportedCurrency( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.FULL_NAME + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + .umaDomain("platform.uma.domain") + .updatedAt(OffsetDateTime.parse("2025-06-15T12:30:45Z")) + .webhookEndpoint("https://api.mycompany.com/webhooks/uma") + .build() + + val roundtrippedPlatformConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(platformConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedPlatformConfig).isEqualTo(platformConfig) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformCurrencyConfigTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformCurrencyConfigTest.kt new file mode 100644 index 00000000..3305783c --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/config/PlatformCurrencyConfigTest.kt @@ -0,0 +1,118 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.config + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.receiver.CounterpartyFieldDefinition +import com.grid.api.models.transactions.TransactionType +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlatformCurrencyConfigTest { + + @Test + fun create() { + val platformCurrencyConfig = + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField(CustomerInfoFieldName.FULL_NAME) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + + assertThat(platformCurrencyConfig.currencyCode()).isEqualTo("USD") + assertThat(platformCurrencyConfig.enabledTransactionTypes()) + .containsExactly(TransactionType.OUTGOING, TransactionType.INCOMING) + assertThat(platformCurrencyConfig.maxAmount()).isEqualTo(1000000L) + assertThat(platformCurrencyConfig.minAmount()).isEqualTo(100L) + assertThat(platformCurrencyConfig.requiredCounterpartyFields()) + .containsExactly( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + ) + assertThat(platformCurrencyConfig.providerRequiredCounterpartyCustomerFields()) + .containsExactly( + CustomerInfoFieldName.FULL_NAME, + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE, + ) + assertThat(platformCurrencyConfig.providerRequiredCustomerFields()) + .containsExactly(CustomerInfoFieldName.NATIONALITY, CustomerInfoFieldName.BIRTH_DATE) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val platformCurrencyConfig = + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField(CustomerInfoFieldName.FULL_NAME) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + + val roundtrippedPlatformCurrencyConfig = + jsonMapper.readValue( + jsonMapper.writeValueAsString(platformCurrencyConfig), + jacksonTypeRef(), + ) + + assertThat(roundtrippedPlatformCurrencyConfig).isEqualTo(platformCurrencyConfig) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateParamsTest.kt new file mode 100644 index 00000000..f8db9195 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateParamsTest.kt @@ -0,0 +1,121 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerCreateParamsTest { + + @Test + fun create() { + CustomerCreateParams.builder() + .createCustomerRequest( + CustomerCreateParams.CreateCustomerRequest.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerCreateParams.CreateCustomerRequest.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + .build() + } + + @Test + fun body() { + val params = + CustomerCreateParams.builder() + .createCustomerRequest( + CustomerCreateParams.CreateCustomerRequest.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerCreateParams.CreateCustomerRequest.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + CustomerCreateParams.CreateCustomerRequest.ofIndividual( + CustomerCreateParams.CreateCustomerRequest.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerCreateParams.CreateCustomerRequest.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + CustomerCreateParams.builder() + .individualCreateCustomerRequest("9f84e0c2a72c4fa") + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + CustomerCreateParams.CreateCustomerRequest.ofIndividual( + CustomerCreateParams.CreateCustomerRequest.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .build() + ) + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateTest.kt new file mode 100644 index 00000000..09615bec --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerCreateTest.kt @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerCreateTest { + + @Test + fun create() { + val customerCreate = + CustomerCreate.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .build() + + assertThat(customerCreate.platformCustomerId()).isEqualTo("9f84e0c2a72c4fa") + assertThat(customerCreate.umaAddress()).isEqualTo("\$john.doe@uma.domain.com") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val customerCreate = + CustomerCreate.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .build() + + val roundtrippedCustomerCreate = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerCreate), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerCreate).isEqualTo(customerCreate) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteParamsTest.kt new file mode 100644 index 00000000..7d2cec0a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerDeleteParamsTest { + + @Test + fun create() { + CustomerDeleteParams.builder().customerId("customerId").build() + } + + @Test + fun pathParams() { + val params = CustomerDeleteParams.builder().customerId("customerId").build() + + assertThat(params._pathParam(0)).isEqualTo("customerId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteResponseTest.kt new file mode 100644 index 00000000..01ac40cd --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerDeleteResponseTest.kt @@ -0,0 +1,239 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class CustomerDeleteResponseTest { + + @Test + fun ofIndividual() { + val individual = + CustomerDeleteResponse.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerDeleteResponse.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerDeleteResponse.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + + val customerDeleteResponse = CustomerDeleteResponse.ofIndividual(individual) + + assertThat(customerDeleteResponse.individual()).isEqualTo(individual) + assertThat(customerDeleteResponse.business()).isNull() + } + + @Test + fun ofIndividualRoundtrip() { + val jsonMapper = jsonMapper() + val customerDeleteResponse = + CustomerDeleteResponse.ofIndividual( + CustomerDeleteResponse.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerDeleteResponse.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerDeleteResponse.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + + val roundtrippedCustomerDeleteResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerDeleteResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerDeleteResponse).isEqualTo(customerDeleteResponse) + } + + @Test + fun ofBusiness() { + val business = + CustomerDeleteResponse.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerDeleteResponse.Business.CustomerType.BUSINESS) + .address( + CustomerDeleteResponse.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerDeleteResponse.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerDeleteResponse.Business.BeneficialOwner.IndividualType.DIRECTOR + ) + .address( + CustomerDeleteResponse.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerDeleteResponse.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + + val customerDeleteResponse = CustomerDeleteResponse.ofBusiness(business) + + assertThat(customerDeleteResponse.individual()).isNull() + assertThat(customerDeleteResponse.business()).isEqualTo(business) + } + + @Test + fun ofBusinessRoundtrip() { + val jsonMapper = jsonMapper() + val customerDeleteResponse = + CustomerDeleteResponse.ofBusiness( + CustomerDeleteResponse.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerDeleteResponse.Business.CustomerType.BUSINESS) + .address( + CustomerDeleteResponse.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerDeleteResponse.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerDeleteResponse.Business.BeneficialOwner.IndividualType + .DIRECTOR + ) + .address( + CustomerDeleteResponse.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerDeleteResponse.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + ) + + val roundtrippedCustomerDeleteResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerDeleteResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerDeleteResponse).isEqualTo(customerDeleteResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val customerDeleteResponse = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { customerDeleteResponse.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParamsTest.kt new file mode 100644 index 00000000..fca371f3 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkParamsTest.kt @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerGetKycLinkParamsTest { + + @Test + fun create() { + CustomerGetKycLinkParams.builder() + .platformCustomerId("platformCustomerId") + .redirectUri("redirectUri") + .build() + } + + @Test + fun queryParams() { + val params = + CustomerGetKycLinkParams.builder() + .platformCustomerId("platformCustomerId") + .redirectUri("redirectUri") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("platformCustomerId", "platformCustomerId") + .put("redirectUri", "redirectUri") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = + CustomerGetKycLinkParams.builder().platformCustomerId("platformCustomerId").build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder().put("platformCustomerId", "platformCustomerId").build() + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponseTest.kt new file mode 100644 index 00000000..57c07442 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerGetKycLinkResponseTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerGetKycLinkResponseTest { + + @Test + fun create() { + val customerGetKycLinkResponse = + CustomerGetKycLinkResponse.builder() + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .kycUrl("https://example.com/redirect") + .platformCustomerId("019542f5-b3e7-1d02-0000-000000000001") + .build() + + assertThat(customerGetKycLinkResponse.customerId()) + .isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(customerGetKycLinkResponse.kycUrl()).isEqualTo("https://example.com/redirect") + assertThat(customerGetKycLinkResponse.platformCustomerId()) + .isEqualTo("019542f5-b3e7-1d02-0000-000000000001") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val customerGetKycLinkResponse = + CustomerGetKycLinkResponse.builder() + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .kycUrl("https://example.com/redirect") + .platformCustomerId("019542f5-b3e7-1d02-0000-000000000001") + .build() + + val roundtrippedCustomerGetKycLinkResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerGetKycLinkResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerGetKycLinkResponse).isEqualTo(customerGetKycLinkResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponseTest.kt new file mode 100644 index 00000000..4344c99c --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsPageResponseTest.kt @@ -0,0 +1,170 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.Currency +import com.grid.api.models.quotes.PaymentInstructions +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerListInternalAccountsPageResponseTest { + + @Test + fun create() { + val customerListInternalAccountsPageResponse = + CustomerListInternalAccountsPageResponse.builder() + .addData( + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(customerListInternalAccountsPageResponse.data()) + .containsExactly( + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + assertThat(customerListInternalAccountsPageResponse.hasMore()).isEqualTo(true) + assertThat(customerListInternalAccountsPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(customerListInternalAccountsPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val customerListInternalAccountsPageResponse = + CustomerListInternalAccountsPageResponse.builder() + .addData( + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedCustomerListInternalAccountsPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerListInternalAccountsPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerListInternalAccountsPageResponse) + .isEqualTo(customerListInternalAccountsPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParamsTest.kt new file mode 100644 index 00000000..25d2d1bc --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListInternalAccountsParamsTest.kt @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerListInternalAccountsParamsTest { + + @Test + fun create() { + CustomerListInternalAccountsParams.builder() + .currency("currency") + .cursor("cursor") + .customerId("customerId") + .limit(1L) + .build() + } + + @Test + fun queryParams() { + val params = + CustomerListInternalAccountsParams.builder() + .currency("currency") + .cursor("cursor") + .customerId("customerId") + .limit(1L) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("currency", "currency") + .put("cursor", "cursor") + .put("customerId", "customerId") + .put("limit", "1") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = CustomerListInternalAccountsParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListPageResponseTest.kt new file mode 100644 index 00000000..c8055ef2 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListPageResponseTest.kt @@ -0,0 +1,124 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.LocalDate +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerListPageResponseTest { + + @Test + fun create() { + val customerListPageResponse = + CustomerListPageResponse.builder() + .addData( + CustomerOneOf.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(customerListPageResponse.data()) + .containsExactly( + CustomerOneOf.ofIndividual( + CustomerOneOf.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + ) + assertThat(customerListPageResponse.hasMore()).isEqualTo(true) + assertThat(customerListPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(customerListPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val customerListPageResponse = + CustomerListPageResponse.builder() + .addData( + CustomerOneOf.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedCustomerListPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerListPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerListPageResponse).isEqualTo(customerListPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListParamsTest.kt new file mode 100644 index 00000000..41981a28 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerListParamsTest.kt @@ -0,0 +1,71 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.grid.api.core.http.QueryParams +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerListParamsTest { + + @Test + fun create() { + CustomerListParams.builder() + .createdAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .createdBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .cursor("cursor") + .customerType(CustomerListParams.CustomerType.INDIVIDUAL) + .isIncludingDeleted(true) + .limit(1L) + .platformCustomerId("platformCustomerId") + .umaAddress("umaAddress") + .updatedAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .updatedBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + } + + @Test + fun queryParams() { + val params = + CustomerListParams.builder() + .createdAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .createdBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .cursor("cursor") + .customerType(CustomerListParams.CustomerType.INDIVIDUAL) + .isIncludingDeleted(true) + .limit(1L) + .platformCustomerId("platformCustomerId") + .umaAddress("umaAddress") + .updatedAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .updatedBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("createdAfter", "2019-12-27T18:11:19.117Z") + .put("createdBefore", "2019-12-27T18:11:19.117Z") + .put("cursor", "cursor") + .put("customerType", "INDIVIDUAL") + .put("isIncludingDeleted", "true") + .put("limit", "1") + .put("platformCustomerId", "platformCustomerId") + .put("umaAddress", "umaAddress") + .put("updatedAfter", "2019-12-27T18:11:19.117Z") + .put("updatedBefore", "2019-12-27T18:11:19.117Z") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = CustomerListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerOneOfTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerOneOfTest.kt new file mode 100644 index 00000000..c8d9ed3e --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerOneOfTest.kt @@ -0,0 +1,238 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class CustomerOneOfTest { + + @Test + fun ofIndividual() { + val individual = + CustomerOneOf.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + + val customerOneOf = CustomerOneOf.ofIndividual(individual) + + assertThat(customerOneOf.individual()).isEqualTo(individual) + assertThat(customerOneOf.business()).isNull() + } + + @Test + fun ofIndividualRoundtrip() { + val jsonMapper = jsonMapper() + val customerOneOf = + CustomerOneOf.ofIndividual( + CustomerOneOf.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + + val roundtrippedCustomerOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerOneOf).isEqualTo(customerOneOf) + } + + @Test + fun ofBusiness() { + val business = + CustomerOneOf.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Business.CustomerType.BUSINESS) + .address( + CustomerOneOf.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerOneOf.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerOneOf.Business.BeneficialOwner.IndividualType.DIRECTOR + ) + .address( + CustomerOneOf.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerOneOf.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + + val customerOneOf = CustomerOneOf.ofBusiness(business) + + assertThat(customerOneOf.individual()).isNull() + assertThat(customerOneOf.business()).isEqualTo(business) + } + + @Test + fun ofBusinessRoundtrip() { + val jsonMapper = jsonMapper() + val customerOneOf = + CustomerOneOf.ofBusiness( + CustomerOneOf.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerOneOf.Business.CustomerType.BUSINESS) + .address( + CustomerOneOf.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerOneOf.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerOneOf.Business.BeneficialOwner.IndividualType.DIRECTOR + ) + .address( + CustomerOneOf.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerOneOf.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + ) + + val roundtrippedCustomerOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerOneOf).isEqualTo(customerOneOf) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val customerOneOf = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { customerOneOf.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveParamsTest.kt new file mode 100644 index 00000000..27804784 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerRetrieveParamsTest { + + @Test + fun create() { + CustomerRetrieveParams.builder().customerId("customerId").build() + } + + @Test + fun pathParams() { + val params = CustomerRetrieveParams.builder().customerId("customerId").build() + + assertThat(params._pathParam(0)).isEqualTo("customerId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveResponseTest.kt new file mode 100644 index 00000000..b555c884 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerRetrieveResponseTest.kt @@ -0,0 +1,240 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class CustomerRetrieveResponseTest { + + @Test + fun ofIndividual() { + val individual = + CustomerRetrieveResponse.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerRetrieveResponse.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerRetrieveResponse.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + + val customerRetrieveResponse = CustomerRetrieveResponse.ofIndividual(individual) + + assertThat(customerRetrieveResponse.individual()).isEqualTo(individual) + assertThat(customerRetrieveResponse.business()).isNull() + } + + @Test + fun ofIndividualRoundtrip() { + val jsonMapper = jsonMapper() + val customerRetrieveResponse = + CustomerRetrieveResponse.ofIndividual( + CustomerRetrieveResponse.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerRetrieveResponse.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerRetrieveResponse.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + + val roundtrippedCustomerRetrieveResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerRetrieveResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerRetrieveResponse).isEqualTo(customerRetrieveResponse) + } + + @Test + fun ofBusiness() { + val business = + CustomerRetrieveResponse.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerRetrieveResponse.Business.CustomerType.BUSINESS) + .address( + CustomerRetrieveResponse.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerRetrieveResponse.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerRetrieveResponse.Business.BeneficialOwner.IndividualType + .DIRECTOR + ) + .address( + CustomerRetrieveResponse.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerRetrieveResponse.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + + val customerRetrieveResponse = CustomerRetrieveResponse.ofBusiness(business) + + assertThat(customerRetrieveResponse.individual()).isNull() + assertThat(customerRetrieveResponse.business()).isEqualTo(business) + } + + @Test + fun ofBusinessRoundtrip() { + val jsonMapper = jsonMapper() + val customerRetrieveResponse = + CustomerRetrieveResponse.ofBusiness( + CustomerRetrieveResponse.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerRetrieveResponse.Business.CustomerType.BUSINESS) + .address( + CustomerRetrieveResponse.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerRetrieveResponse.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerRetrieveResponse.Business.BeneficialOwner.IndividualType + .DIRECTOR + ) + .address( + CustomerRetrieveResponse.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerRetrieveResponse.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + ) + + val roundtrippedCustomerRetrieveResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerRetrieveResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerRetrieveResponse).isEqualTo(customerRetrieveResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val customerRetrieveResponse = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { customerRetrieveResponse.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerTest.kt new file mode 100644 index 00000000..a1671f64 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerTest.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerTest { + + @Test + fun create() { + val customer = + Customer.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .build() + + assertThat(customer.platformCustomerId()).isEqualTo("9f84e0c2a72c4fa") + assertThat(customer.umaAddress()).isEqualTo("\$john.doe@uma.domain.com") + assertThat(customer.id()).isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(customer.createdAt()).isEqualTo(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + assertThat(customer.isDeleted()).isEqualTo(false) + assertThat(customer.kycStatus()).isEqualTo(Customer.KycStatus.APPROVED) + assertThat(customer.updatedAt()).isEqualTo(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val customer = + Customer.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .build() + + val roundtrippedCustomer = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customer), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomer).isEqualTo(customer) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateParamsTest.kt new file mode 100644 index 00000000..1832977c --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateParamsTest.kt @@ -0,0 +1,147 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerUpdateParamsTest { + + @Test + fun create() { + CustomerUpdateParams.builder() + .customerId("customerId") + .updateCustomerRequest( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerUpdateParams.UpdateCustomerRequest.Individual.Address.builder() + .country("US") + .line1("456 Market St") + .postalCode("94103") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1985-06-15")) + .fullName("John Smith") + .nationality("US") + .build() + ) + .build() + } + + @Test + fun pathParams() { + val params = + CustomerUpdateParams.builder() + .customerId("customerId") + .updateCustomerRequest( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .build() + ) + .build() + + assertThat(params._pathParam(0)).isEqualTo("customerId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + CustomerUpdateParams.builder() + .customerId("customerId") + .updateCustomerRequest( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerUpdateParams.UpdateCustomerRequest.Individual.Address.builder() + .country("US") + .line1("456 Market St") + .postalCode("94103") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1985-06-15")) + .fullName("John Smith") + .nationality("US") + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + CustomerUpdateParams.UpdateCustomerRequest.ofIndividual( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerUpdateParams.UpdateCustomerRequest.Individual.Address.builder() + .country("US") + .line1("456 Market St") + .postalCode("94103") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1985-06-15")) + .fullName("John Smith") + .nationality("US") + .build() + ) + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + CustomerUpdateParams.builder() + .customerId("customerId") + .updateCustomerRequest( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + CustomerUpdateParams.UpdateCustomerRequest.ofIndividual( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .build() + ) + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateResponseTest.kt new file mode 100644 index 00000000..50aa868e --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateResponseTest.kt @@ -0,0 +1,239 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class CustomerUpdateResponseTest { + + @Test + fun ofIndividual() { + val individual = + CustomerUpdateResponse.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerUpdateResponse.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerUpdateResponse.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + + val customerUpdateResponse = CustomerUpdateResponse.ofIndividual(individual) + + assertThat(customerUpdateResponse.individual()).isEqualTo(individual) + assertThat(customerUpdateResponse.business()).isNull() + } + + @Test + fun ofIndividualRoundtrip() { + val jsonMapper = jsonMapper() + val customerUpdateResponse = + CustomerUpdateResponse.ofIndividual( + CustomerUpdateResponse.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerUpdateResponse.Individual.CustomerType.INDIVIDUAL) + .address( + CustomerUpdateResponse.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + + val roundtrippedCustomerUpdateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerUpdateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerUpdateResponse).isEqualTo(customerUpdateResponse) + } + + @Test + fun ofBusiness() { + val business = + CustomerUpdateResponse.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerUpdateResponse.Business.CustomerType.BUSINESS) + .address( + CustomerUpdateResponse.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerUpdateResponse.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerUpdateResponse.Business.BeneficialOwner.IndividualType.DIRECTOR + ) + .address( + CustomerUpdateResponse.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerUpdateResponse.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + + val customerUpdateResponse = CustomerUpdateResponse.ofBusiness(business) + + assertThat(customerUpdateResponse.individual()).isNull() + assertThat(customerUpdateResponse.business()).isEqualTo(business) + } + + @Test + fun ofBusinessRoundtrip() { + val jsonMapper = jsonMapper() + val customerUpdateResponse = + CustomerUpdateResponse.ofBusiness( + CustomerUpdateResponse.Business.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .id("Customer:019542f5-b3e7-1d02-0000-000000000001") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .isDeleted(false) + .kycStatus(Customer.KycStatus.APPROVED) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .customerType(CustomerUpdateResponse.Business.CustomerType.BUSINESS) + .address( + CustomerUpdateResponse.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .addBeneficialOwner( + CustomerUpdateResponse.Business.BeneficialOwner.builder() + .fullName("John Michael Doe") + .individualType( + CustomerUpdateResponse.Business.BeneficialOwner.IndividualType + .DIRECTOR + ) + .address( + CustomerUpdateResponse.Business.BeneficialOwner.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .emailAddress("example@test.com") + .nationality("US") + .percentageOwnership(25.0) + .phoneNumber("+5555555555") + .taxId("EIN-987654321") + .title("CEO, COO, President") + .build() + ) + .businessInfo( + CustomerUpdateResponse.Business.BusinessInfo.builder() + .legalName("Acme Corporation, Inc.") + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + .build() + ) + + val roundtrippedCustomerUpdateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerUpdateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerUpdateResponse).isEqualTo(customerUpdateResponse) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val customerUpdateResponse = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { customerUpdateResponse.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateTest.kt new file mode 100644 index 00000000..dec8e607 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/CustomerUpdateTest.kt @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CustomerUpdateTest { + + @Test + fun create() { + val customerUpdate = + CustomerUpdate.builder().umaAddress("\$john.doe@uma.domain.com").build() + + assertThat(customerUpdate.umaAddress()).isEqualTo("\$john.doe@uma.domain.com") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val customerUpdate = + CustomerUpdate.builder().umaAddress("\$john.doe@uma.domain.com").build() + + val roundtrippedCustomerUpdate = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerUpdate), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerUpdate).isEqualTo(customerUpdate) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParamsTest.kt new file mode 100644 index 00000000..4289743f --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BulkGetJobStatusParamsTest { + + @Test + fun create() { + BulkGetJobStatusParams.builder().jobId("jobId").build() + } + + @Test + fun pathParams() { + val params = BulkGetJobStatusParams.builder().jobId("jobId").build() + + assertThat(params._pathParam(0)).isEqualTo("jobId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponseTest.kt new file mode 100644 index 00000000..074a675a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkGetJobStatusResponseTest.kt @@ -0,0 +1,111 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BulkGetJobStatusResponseTest { + + @Test + fun create() { + val bulkGetJobStatusResponse = + BulkGetJobStatusResponse.builder() + .jobId("Job:019542f5-b3e7-1d02-0000-000000000006") + .progress( + BulkGetJobStatusResponse.Progress.builder() + .failed(50L) + .processed(2500L) + .successful(2450L) + .total(5000L) + .build() + ) + .status(BulkGetJobStatusResponse.Status.PROCESSING) + .completedAt(OffsetDateTime.parse("2025-08-15T14:32:00Z")) + .addError( + BulkGetJobStatusResponse.Error.builder() + .correlationId("biz456") + .code("code") + .details( + BulkGetJobStatusResponse.Error.Details.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .message("message") + .build() + ) + .build() + + assertThat(bulkGetJobStatusResponse.jobId()) + .isEqualTo("Job:019542f5-b3e7-1d02-0000-000000000006") + assertThat(bulkGetJobStatusResponse.progress()) + .isEqualTo( + BulkGetJobStatusResponse.Progress.builder() + .failed(50L) + .processed(2500L) + .successful(2450L) + .total(5000L) + .build() + ) + assertThat(bulkGetJobStatusResponse.status()) + .isEqualTo(BulkGetJobStatusResponse.Status.PROCESSING) + assertThat(bulkGetJobStatusResponse.completedAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:32:00Z")) + assertThat(bulkGetJobStatusResponse.errors()) + .containsExactly( + BulkGetJobStatusResponse.Error.builder() + .correlationId("biz456") + .code("code") + .details( + BulkGetJobStatusResponse.Error.Details.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .message("message") + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val bulkGetJobStatusResponse = + BulkGetJobStatusResponse.builder() + .jobId("Job:019542f5-b3e7-1d02-0000-000000000006") + .progress( + BulkGetJobStatusResponse.Progress.builder() + .failed(50L) + .processed(2500L) + .successful(2450L) + .total(5000L) + .build() + ) + .status(BulkGetJobStatusResponse.Status.PROCESSING) + .completedAt(OffsetDateTime.parse("2025-08-15T14:32:00Z")) + .addError( + BulkGetJobStatusResponse.Error.builder() + .correlationId("biz456") + .code("code") + .details( + BulkGetJobStatusResponse.Error.Details.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .message("message") + .build() + ) + .build() + + val roundtrippedBulkGetJobStatusResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(bulkGetJobStatusResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBulkGetJobStatusResponse).isEqualTo(bulkGetJobStatusResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParamsTest.kt new file mode 100644 index 00000000..90b3b4f1 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvParamsTest.kt @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.grid.api.core.MultipartField +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BulkUploadCsvParamsTest { + + @Test + fun create() { + BulkUploadCsvParams.builder().file("some content".byteInputStream()).build() + } + + @Test + fun body() { + val params = BulkUploadCsvParams.builder().file("some content".byteInputStream()).build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }) + .usingRecursiveComparison() + // TODO(AssertJ): Replace this and the `mapValues` below with: + // https://github.com/assertj/assertj/issues/3165 + .withEqualsForType( + { a, b -> a.readBytes() contentEquals b.readBytes() }, + InputStream::class.java, + ) + .isEqualTo( + mapOf("file" to MultipartField.of("some content".byteInputStream())).mapValues { + (_, field) -> + field.map { (it as? ByteArray)?.inputStream() ?: it } + } + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponseTest.kt new file mode 100644 index 00000000..8b1bd9f3 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/bulk/BulkUploadCsvResponseTest.kt @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.bulk + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BulkUploadCsvResponseTest { + + @Test + fun create() { + val bulkUploadCsvResponse = + BulkUploadCsvResponse.builder() + .jobId("Job:019542f5-b3e7-1d02-0000-000000000006") + .status(BulkUploadCsvResponse.Status.PENDING) + .build() + + assertThat(bulkUploadCsvResponse.jobId()) + .isEqualTo("Job:019542f5-b3e7-1d02-0000-000000000006") + assertThat(bulkUploadCsvResponse.status()).isEqualTo(BulkUploadCsvResponse.Status.PENDING) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val bulkUploadCsvResponse = + BulkUploadCsvResponse.builder() + .jobId("Job:019542f5-b3e7-1d02-0000-000000000006") + .status(BulkUploadCsvResponse.Status.PENDING) + .build() + + val roundtrippedBulkUploadCsvResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(bulkUploadCsvResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBulkUploadCsvResponse).isEqualTo(bulkUploadCsvResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOfTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOfTest.kt new file mode 100644 index 00000000..76b2e9b1 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/BeneficiaryOneOfTest.kt @@ -0,0 +1,147 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class BeneficiaryOneOfTest { + + @Test + fun ofIndividual() { + val individual = + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + + val beneficiaryOneOf = BeneficiaryOneOf.ofIndividual(individual) + + assertThat(beneficiaryOneOf.individual()).isEqualTo(individual) + assertThat(beneficiaryOneOf.business()).isNull() + } + + @Test + fun ofIndividualRoundtrip() { + val jsonMapper = jsonMapper() + val beneficiaryOneOf = + BeneficiaryOneOf.ofIndividual( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + + val roundtrippedBeneficiaryOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(beneficiaryOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBeneficiaryOneOf).isEqualTo(beneficiaryOneOf) + } + + @Test + fun ofBusiness() { + val business = + BeneficiaryOneOf.Business.builder() + .legalName("Acme Corporation, Inc.") + .address( + BeneficiaryOneOf.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + + val beneficiaryOneOf = BeneficiaryOneOf.ofBusiness(business) + + assertThat(beneficiaryOneOf.individual()).isNull() + assertThat(beneficiaryOneOf.business()).isEqualTo(business) + } + + @Test + fun ofBusinessRoundtrip() { + val jsonMapper = jsonMapper() + val beneficiaryOneOf = + BeneficiaryOneOf.ofBusiness( + BeneficiaryOneOf.Business.builder() + .legalName("Acme Corporation, Inc.") + .address( + BeneficiaryOneOf.Business.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .registrationNumber("BRN-123456789") + .taxId("EIN-987654321") + .build() + ) + + val roundtrippedBeneficiaryOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(beneficiaryOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBeneficiaryOneOf).isEqualTo(beneficiaryOneOf) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val beneficiaryOneOf = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { beneficiaryOneOf.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParamsTest.kt new file mode 100644 index 00000000..f930b61a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateParamsTest.kt @@ -0,0 +1,185 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountCreateParamsTest { + + @Test + fun create() { + ExternalAccountCreateParams.builder() + .externalAccountCreate( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + .build() + } + + @Test + fun body() { + val params = + ExternalAccountCreateParams.builder() + .externalAccountCreate( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ExternalAccountCreateParams.builder() + .externalAccountCreate( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .build() + ) + .routingNumber("123456789") + .build() + ) + .currency("USD") + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .build() + ) + .routingNumber("123456789") + .build() + ) + .currency("USD") + .build() + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateTest.kt new file mode 100644 index 00000000..442525e9 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountCreateTest.kt @@ -0,0 +1,133 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountCreateTest { + + @Test + fun create() { + val externalAccountCreate = + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + + assertThat(externalAccountCreate.accountInfo()) + .isEqualTo( + ExternalAccountInfoOneOf.ofUsAccount( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + ) + assertThat(externalAccountCreate.currency()).isEqualTo("USD") + assertThat(externalAccountCreate.customerId()) + .isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(externalAccountCreate.defaultUmaDepositAccount()).isEqualTo(true) + assertThat(externalAccountCreate.platformAccountId()).isEqualTo("ext_acc_123456") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val externalAccountCreate = + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + + val roundtrippedExternalAccountCreate = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountCreate), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountCreate).isEqualTo(externalAccountCreate) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOfTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOfTest.kt new file mode 100644 index 00000000..5b3e3475 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountInfoOneOfTest.kt @@ -0,0 +1,1155 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class ExternalAccountInfoOneOfTest { + + @Test + fun ofUsAccount() { + val usAccount = + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory(ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofUsAccount(usAccount) + + assertThat(externalAccountInfoOneOf.usAccount()).isEqualTo(usAccount) + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofUsAccountRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofUsAccount( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory(ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofClabe() { + val clabe = + ExternalAccountInfoOneOf.Clabe.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .clabeNumber("123456789012345678") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofClabe(clabe) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isEqualTo(clabe) + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofClabeRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofClabe( + ExternalAccountInfoOneOf.Clabe.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .clabeNumber("123456789012345678") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofPix() { + val pix = + ExternalAccountInfoOneOf.Pix.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .pixKey("55119876543210") + .pixKeyType(ExternalAccountInfoOneOf.Pix.PixKeyType.PHONE) + .taxId("1234567890") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofPix(pix) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isEqualTo(pix) + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofPixRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofPix( + ExternalAccountInfoOneOf.Pix.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .pixKey("55119876543210") + .pixKeyType(ExternalAccountInfoOneOf.Pix.PixKeyType.PHONE) + .taxId("1234567890") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofIban() { + val iban = + ExternalAccountInfoOneOf.Iban.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .iban("DE89370400440532013000") + .swiftBic("DEUTDEFF") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofIban(iban) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isEqualTo(iban) + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofIbanRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofIban( + ExternalAccountInfoOneOf.Iban.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .iban("DE89370400440532013000") + .swiftBic("DEUTDEFF") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofUpi() { + val upi = + ExternalAccountInfoOneOf.Upi.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .vpa("somecustomers@okbank") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofUpi(upi) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isEqualTo(upi) + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofUpiRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofUpi( + ExternalAccountInfoOneOf.Upi.builder() + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .vpa("somecustomers@okbank") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofNgnAccount() { + val ngnAccount = + ExternalAccountInfoOneOf.NgnAccount.builder() + .accountNumber("0123456789") + .bankName("First Bank of Nigeria") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .purposeOfPayment( + ExternalAccountInfoOneOf.NgnAccount.PurposeOfPayment.GOODS_OR_SERVICES + ) + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofNgnAccount(ngnAccount) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isEqualTo(ngnAccount) + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofNgnAccountRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofNgnAccount( + ExternalAccountInfoOneOf.NgnAccount.builder() + .accountNumber("0123456789") + .bankName("First Bank of Nigeria") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .purposeOfPayment( + ExternalAccountInfoOneOf.NgnAccount.PurposeOfPayment.GOODS_OR_SERVICES + ) + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofCadAccount() { + val cadAccount = + ExternalAccountInfoOneOf.CadAccount.builder() + .accountNumber("1234567") + .bankCode("001") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .branchCode("00012") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofCadAccount(cadAccount) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isEqualTo(cadAccount) + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofCadAccountRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofCadAccount( + ExternalAccountInfoOneOf.CadAccount.builder() + .accountNumber("1234567") + .bankCode("001") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .branchCode("00012") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofGbpAccount() { + val gbpAccount = + ExternalAccountInfoOneOf.GbpAccount.builder() + .accountNumber("12345678") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .sortCode("20-00-00") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofGbpAccount(gbpAccount) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isEqualTo(gbpAccount) + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofGbpAccountRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofGbpAccount( + ExternalAccountInfoOneOf.GbpAccount.builder() + .accountNumber("12345678") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .sortCode("20-00-00") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofPhpAccount() { + val phpAccount = + ExternalAccountInfoOneOf.PhpAccount.builder() + .accountNumber("001234567890") + .bankName("BDO Unibank") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofPhpAccount(phpAccount) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isEqualTo(phpAccount) + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofPhpAccountRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofPhpAccount( + ExternalAccountInfoOneOf.PhpAccount.builder() + .accountNumber("001234567890") + .bankName("BDO Unibank") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofSgdAccount() { + val sgdAccount = + ExternalAccountInfoOneOf.SgdAccount.builder() + .accountNumber("0123456789") + .bankName("DBS Bank Ltd") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .swiftCode("DBSSSGSG") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofSgdAccount(sgdAccount) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isEqualTo(sgdAccount) + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofSgdAccountRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofSgdAccount( + ExternalAccountInfoOneOf.SgdAccount.builder() + .accountNumber("0123456789") + .bankName("DBS Bank Ltd") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .swiftCode("DBSSSGSG") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofSparkWallet() { + val sparkWallet = + ExternalAccountInfoOneOf.SparkWallet.builder() + .address("spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofSparkWallet(sparkWallet) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isEqualTo(sparkWallet) + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofSparkWalletRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofSparkWallet( + ExternalAccountInfoOneOf.SparkWallet.builder() + .address("spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofLightning() { + val lightning = + ExternalAccountInfoOneOf.Lightning.builder() + .bolt12( + "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" + ) + .invoice( + "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" + ) + .lightningAddress("john.doe@lightningwallet.com") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofLightning(lightning) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isEqualTo(lightning) + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofLightningRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofLightning( + ExternalAccountInfoOneOf.Lightning.builder() + .bolt12( + "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" + ) + .invoice( + "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" + ) + .lightningAddress("john.doe@lightningwallet.com") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofSolanaWallet() { + val solanaWallet = + ExternalAccountInfoOneOf.SolanaWallet.builder() + .address("4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofSolanaWallet(solanaWallet) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isEqualTo(solanaWallet) + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofSolanaWalletRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofSolanaWallet( + ExternalAccountInfoOneOf.SolanaWallet.builder() + .address("4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofTronWallet() { + val tronWallet = + ExternalAccountInfoOneOf.TronWallet.builder() + .address("TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofTronWallet(tronWallet) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isEqualTo(tronWallet) + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofTronWalletRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofTronWallet( + ExternalAccountInfoOneOf.TronWallet.builder() + .address("TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofPolygonWallet() { + val polygonWallet = + ExternalAccountInfoOneOf.PolygonWallet.builder() + .address("0xAbCDEF1234567890aBCdEf1234567890ABcDef12") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofPolygonWallet(polygonWallet) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isEqualTo(polygonWallet) + assertThat(externalAccountInfoOneOf.baseWallet()).isNull() + } + + @Test + fun ofPolygonWalletRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofPolygonWallet( + ExternalAccountInfoOneOf.PolygonWallet.builder() + .address("0xAbCDEF1234567890aBCdEf1234567890ABcDef12") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + @Test + fun ofBaseWallet() { + val baseWallet = + ExternalAccountInfoOneOf.BaseWallet.builder() + .address("0xAbCDEF1234567890aBCdEf1234567890ABcDef12") + .build() + + val externalAccountInfoOneOf = ExternalAccountInfoOneOf.ofBaseWallet(baseWallet) + + assertThat(externalAccountInfoOneOf.usAccount()).isNull() + assertThat(externalAccountInfoOneOf.clabe()).isNull() + assertThat(externalAccountInfoOneOf.pix()).isNull() + assertThat(externalAccountInfoOneOf.iban()).isNull() + assertThat(externalAccountInfoOneOf.upi()).isNull() + assertThat(externalAccountInfoOneOf.ngnAccount()).isNull() + assertThat(externalAccountInfoOneOf.cadAccount()).isNull() + assertThat(externalAccountInfoOneOf.gbpAccount()).isNull() + assertThat(externalAccountInfoOneOf.phpAccount()).isNull() + assertThat(externalAccountInfoOneOf.sgdAccount()).isNull() + assertThat(externalAccountInfoOneOf.sparkWallet()).isNull() + assertThat(externalAccountInfoOneOf.lightning()).isNull() + assertThat(externalAccountInfoOneOf.solanaWallet()).isNull() + assertThat(externalAccountInfoOneOf.tronWallet()).isNull() + assertThat(externalAccountInfoOneOf.polygonWallet()).isNull() + assertThat(externalAccountInfoOneOf.baseWallet()).isEqualTo(baseWallet) + } + + @Test + fun ofBaseWalletRoundtrip() { + val jsonMapper = jsonMapper() + val externalAccountInfoOneOf = + ExternalAccountInfoOneOf.ofBaseWallet( + ExternalAccountInfoOneOf.BaseWallet.builder() + .address("0xAbCDEF1234567890aBCdEf1234567890ABcDef12") + .build() + ) + + val roundtrippedExternalAccountInfoOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountInfoOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountInfoOneOf).isEqualTo(externalAccountInfoOneOf) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val externalAccountInfoOneOf = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { externalAccountInfoOneOf.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponseTest.kt new file mode 100644 index 00000000..06adcba8 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListPageResponseTest.kt @@ -0,0 +1,158 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountListPageResponseTest { + + @Test + fun create() { + val externalAccountListPageResponse = + ExternalAccountListPageResponse.builder() + .addData( + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(externalAccountListPageResponse.data()) + .containsExactly( + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + ) + assertThat(externalAccountListPageResponse.hasMore()).isEqualTo(true) + assertThat(externalAccountListPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(externalAccountListPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val externalAccountListPageResponse = + ExternalAccountListPageResponse.builder() + .addData( + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedExternalAccountListPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountListPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountListPageResponse) + .isEqualTo(externalAccountListPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParamsTest.kt new file mode 100644 index 00000000..0c42e021 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountListParamsTest.kt @@ -0,0 +1,52 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountListParamsTest { + + @Test + fun create() { + ExternalAccountListParams.builder() + .currency("currency") + .cursor("cursor") + .customerId("customerId") + .limit(1L) + .build() + } + + @Test + fun queryParams() { + val params = + ExternalAccountListParams.builder() + .currency("currency") + .cursor("cursor") + .customerId("customerId") + .limit(1L) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("currency", "currency") + .put("cursor", "cursor") + .put("customerId", "customerId") + .put("limit", "1") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = ExternalAccountListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountTest.kt new file mode 100644 index 00000000..7977b245 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/customers/externalaccounts/ExternalAccountTest.kt @@ -0,0 +1,140 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.customers.externalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountTest { + + @Test + fun create() { + val externalAccount = + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + + assertThat(externalAccount.id()) + .isEqualTo("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + assertThat(externalAccount.accountInfo()) + .isEqualTo( + ExternalAccountInfoOneOf.ofUsAccount( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + ) + assertThat(externalAccount.currency()).isEqualTo("USD") + assertThat(externalAccount.status()).isEqualTo(ExternalAccount.Status.ACTIVE) + assertThat(externalAccount.customerId()) + .isEqualTo("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + assertThat(externalAccount.defaultUmaDepositAccount()).isEqualTo(false) + assertThat(externalAccount.platformAccountId()).isEqualTo("acc_123456789") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val externalAccount = + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + + val roundtrippedExternalAccount = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccount), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccount).isEqualTo(externalAccount) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParamsTest.kt new file mode 100644 index 00000000..50cd8c49 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListParamsTest.kt @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.exchangerates + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExchangeRateListParamsTest { + + @Test + fun create() { + ExchangeRateListParams.builder() + .addDestinationCurrency("string") + .sendingAmount(0L) + .sourceCurrency("sourceCurrency") + .build() + } + + @Test + fun queryParams() { + val params = + ExchangeRateListParams.builder() + .addDestinationCurrency("string") + .sendingAmount(0L) + .sourceCurrency("sourceCurrency") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("destinationCurrency", listOf("string").joinToString(",")) + .put("sendingAmount", "0") + .put("sourceCurrency", "sourceCurrency") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = ExchangeRateListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponseTest.kt new file mode 100644 index 00000000..b0477a0b --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/exchangerates/ExchangeRateListResponseTest.kt @@ -0,0 +1,119 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.exchangerates + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.quotes.Currency +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExchangeRateListResponseTest { + + @Test + fun create() { + val exchangeRateListResponse = + ExchangeRateListResponse.builder() + .addData( + ExchangeRateListResponse.Data.builder() + .destinationCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .destinationPaymentRail("UPI") + .exchangeRate(82.5) + .fees(ExchangeRateListResponse.Data.Fees.builder().fixed(100L).build()) + .receivingAmount(1650000L) + .sourceCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-02-05T12:00:00Z")) + .sendingAmount(10000L) + .sourcePaymentRail("ACCOUNT") + .build() + ) + .build() + + assertThat(exchangeRateListResponse.data()) + .containsExactly( + ExchangeRateListResponse.Data.builder() + .destinationCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .destinationPaymentRail("UPI") + .exchangeRate(82.5) + .fees(ExchangeRateListResponse.Data.Fees.builder().fixed(100L).build()) + .receivingAmount(1650000L) + .sourceCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-02-05T12:00:00Z")) + .sendingAmount(10000L) + .sourcePaymentRail("ACCOUNT") + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val exchangeRateListResponse = + ExchangeRateListResponse.builder() + .addData( + ExchangeRateListResponse.Data.builder() + .destinationCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .destinationPaymentRail("UPI") + .exchangeRate(82.5) + .fees(ExchangeRateListResponse.Data.Fees.builder().fixed(100L).build()) + .receivingAmount(1650000L) + .sourceCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-02-05T12:00:00Z")) + .sendingAmount(10000L) + .sourcePaymentRail("ACCOUNT") + .build() + ) + .build() + + val roundtrippedExchangeRateListResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(exchangeRateListResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExchangeRateListResponse).isEqualTo(exchangeRateListResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/CurrencyAmountTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/CurrencyAmountTest.kt new file mode 100644 index 00000000..2fed69b3 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/CurrencyAmountTest.kt @@ -0,0 +1,64 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.quotes.Currency +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CurrencyAmountTest { + + @Test + fun create() { + val currencyAmount = + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + + assertThat(currencyAmount.amount()).isEqualTo(12550L) + assertThat(currencyAmount.currency()) + .isEqualTo( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val currencyAmount = + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + + val roundtrippedCurrencyAmount = + jsonMapper.readValue( + jsonMapper.writeValueAsString(currencyAmount), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCurrencyAmount).isEqualTo(currencyAmount) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCancelParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCancelParamsTest.kt new file mode 100644 index 00000000..c5053750 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCancelParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InvitationCancelParamsTest { + + @Test + fun create() { + InvitationCancelParams.builder().invitationCode("invitationCode").build() + } + + @Test + fun pathParams() { + val params = InvitationCancelParams.builder().invitationCode("invitationCode").build() + + assertThat(params._pathParam(0)).isEqualTo("invitationCode") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationClaimParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationClaimParamsTest.kt new file mode 100644 index 00000000..e3c53d83 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationClaimParamsTest.kt @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InvitationClaimParamsTest { + + @Test + fun create() { + InvitationClaimParams.builder() + .invitationCode("invitationCode") + .inviteeUma("\$invitee@uma.domain") + .build() + } + + @Test + fun pathParams() { + val params = + InvitationClaimParams.builder() + .invitationCode("invitationCode") + .inviteeUma("\$invitee@uma.domain") + .build() + + assertThat(params._pathParam(0)).isEqualTo("invitationCode") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + InvitationClaimParams.builder() + .invitationCode("invitationCode") + .inviteeUma("\$invitee@uma.domain") + .build() + + val body = params._body() + + assertThat(body.inviteeUma()).isEqualTo("\$invitee@uma.domain") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCreateParamsTest.kt new file mode 100644 index 00000000..a0b37200 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationCreateParamsTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InvitationCreateParamsTest { + + @Test + fun create() { + InvitationCreateParams.builder() + .inviterUma("\$inviter@uma.domain") + .amountToSend(12550L) + .expiresAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .firstName("Alice") + .build() + } + + @Test + fun body() { + val params = + InvitationCreateParams.builder() + .inviterUma("\$inviter@uma.domain") + .amountToSend(12550L) + .expiresAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .firstName("Alice") + .build() + + val body = params._body() + + assertThat(body.inviterUma()).isEqualTo("\$inviter@uma.domain") + assertThat(body.amountToSend()).isEqualTo(12550L) + assertThat(body.expiresAt()).isEqualTo(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + assertThat(body.firstName()).isEqualTo("Alice") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = InvitationCreateParams.builder().inviterUma("\$inviter@uma.domain").build() + + val body = params._body() + + assertThat(body.inviterUma()).isEqualTo("\$inviter@uma.domain") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationRetrieveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationRetrieveParamsTest.kt new file mode 100644 index 00000000..bc171f72 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/InvitationRetrieveParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InvitationRetrieveParamsTest { + + @Test + fun create() { + InvitationRetrieveParams.builder().invitationCode("invitationCode").build() + } + + @Test + fun pathParams() { + val params = InvitationRetrieveParams.builder().invitationCode("invitationCode").build() + + assertThat(params._pathParam(0)).isEqualTo("invitationCode") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/UmaInvitationTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/UmaInvitationTest.kt new file mode 100644 index 00000000..1191b743 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/invitations/UmaInvitationTest.kt @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.invitations + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.quotes.Currency +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UmaInvitationTest { + + @Test + fun create() { + val umaInvitation = + UmaInvitation.builder() + .code("019542f5") + .createdAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .inviterUma("\$inviter@uma.domain") + .status(UmaInvitation.Status.PENDING) + .url("https://uma.me/i/019542f5") + .amountToSend( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .claimedAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .expiresAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .firstName("Jane") + .inviteeUma("\$invitee@uma.domain") + .build() + + assertThat(umaInvitation.code()).isEqualTo("019542f5") + assertThat(umaInvitation.createdAt()) + .isEqualTo(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + assertThat(umaInvitation.inviterUma()).isEqualTo("\$inviter@uma.domain") + assertThat(umaInvitation.status()).isEqualTo(UmaInvitation.Status.PENDING) + assertThat(umaInvitation.url()).isEqualTo("https://uma.me/i/019542f5") + assertThat(umaInvitation.amountToSend()) + .isEqualTo( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + assertThat(umaInvitation.claimedAt()) + .isEqualTo(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + assertThat(umaInvitation.expiresAt()) + .isEqualTo(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + assertThat(umaInvitation.firstName()).isEqualTo("Jane") + assertThat(umaInvitation.inviteeUma()).isEqualTo("\$invitee@uma.domain") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val umaInvitation = + UmaInvitation.builder() + .code("019542f5") + .createdAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .inviterUma("\$inviter@uma.domain") + .status(UmaInvitation.Status.PENDING) + .url("https://uma.me/i/019542f5") + .amountToSend( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .claimedAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .expiresAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .firstName("Jane") + .inviteeUma("\$invitee@uma.domain") + .build() + + val roundtrippedUmaInvitation = + jsonMapper.readValue( + jsonMapper.writeValueAsString(umaInvitation), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUmaInvitation).isEqualTo(umaInvitation) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParamsTest.kt new file mode 100644 index 00000000..1ed63e17 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenParamsTest.kt @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.plaid + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlaidCreateLinkTokenParamsTest { + + @Test + fun create() { + PlaidCreateLinkTokenParams.builder() + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + } + + @Test + fun body() { + val params = + PlaidCreateLinkTokenParams.builder() + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + + val body = params._body() + + assertThat(body.customerId()).isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponseTest.kt new file mode 100644 index 00000000..050b3d54 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidCreateLinkTokenResponseTest.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.plaid + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlaidCreateLinkTokenResponseTest { + + @Test + fun create() { + val plaidCreateLinkTokenResponse = + PlaidCreateLinkTokenResponse.builder() + .callbackUrl( + "https://api.lightspark.com/grid/2025-10-13/plaid/callback/{plaid_link_token}" + ) + .expiration(OffsetDateTime.parse("2025-10-05T18:30:00Z")) + .linkToken("link-sandbox-af1a0311-da53-4636-b754-dd15cc058176") + .requestId("req_abc123def456") + .build() + + assertThat(plaidCreateLinkTokenResponse.callbackUrl()) + .isEqualTo( + "https://api.lightspark.com/grid/2025-10-13/plaid/callback/{plaid_link_token}" + ) + assertThat(plaidCreateLinkTokenResponse.expiration()) + .isEqualTo(OffsetDateTime.parse("2025-10-05T18:30:00Z")) + assertThat(plaidCreateLinkTokenResponse.linkToken()) + .isEqualTo("link-sandbox-af1a0311-da53-4636-b754-dd15cc058176") + assertThat(plaidCreateLinkTokenResponse.requestId()).isEqualTo("req_abc123def456") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val plaidCreateLinkTokenResponse = + PlaidCreateLinkTokenResponse.builder() + .callbackUrl( + "https://api.lightspark.com/grid/2025-10-13/plaid/callback/{plaid_link_token}" + ) + .expiration(OffsetDateTime.parse("2025-10-05T18:30:00Z")) + .linkToken("link-sandbox-af1a0311-da53-4636-b754-dd15cc058176") + .requestId("req_abc123def456") + .build() + + val roundtrippedPlaidCreateLinkTokenResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(plaidCreateLinkTokenResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedPlaidCreateLinkTokenResponse).isEqualTo(plaidCreateLinkTokenResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParamsTest.kt new file mode 100644 index 00000000..cc8f5f2d --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/plaid/PlaidSubmitPublicTokenParamsTest.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.plaid + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlaidSubmitPublicTokenParamsTest { + + @Test + fun create() { + PlaidSubmitPublicTokenParams.builder() + .plaidLinkToken("link-sandbox-abc123xyz-1234-5678") + .publicToken("public-sandbox-12345678-1234-1234-1234-123456789012") + .accountId("plaid_account_id_123") + .build() + } + + @Test + fun pathParams() { + val params = + PlaidSubmitPublicTokenParams.builder() + .plaidLinkToken("link-sandbox-abc123xyz-1234-5678") + .publicToken("public-sandbox-12345678-1234-1234-1234-123456789012") + .build() + + assertThat(params._pathParam(0)).isEqualTo("link-sandbox-abc123xyz-1234-5678") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + PlaidSubmitPublicTokenParams.builder() + .plaidLinkToken("link-sandbox-abc123xyz-1234-5678") + .publicToken("public-sandbox-12345678-1234-1234-1234-123456789012") + .accountId("plaid_account_id_123") + .build() + + val body = params._body() + + assertThat(body.publicToken()) + .isEqualTo("public-sandbox-12345678-1234-1234-1234-123456789012") + assertThat(body.accountId()).isEqualTo("plaid_account_id_123") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + PlaidSubmitPublicTokenParams.builder() + .plaidLinkToken("link-sandbox-abc123xyz-1234-5678") + .publicToken("public-sandbox-12345678-1234-1234-1234-123456789012") + .build() + + val body = params._body() + + assertThat(body.publicToken()) + .isEqualTo("public-sandbox-12345678-1234-1234-1234-123456789012") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParamsTest.kt new file mode 100644 index 00000000..291d3561 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsParamsTest.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlatformListInternalAccountsParamsTest { + + @Test + fun create() { + PlatformListInternalAccountsParams.builder().currency("currency").build() + } + + @Test + fun queryParams() { + val params = PlatformListInternalAccountsParams.builder().currency("currency").build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().put("currency", "currency").build()) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = PlatformListInternalAccountsParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponseTest.kt new file mode 100644 index 00000000..e2e6727a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/PlatformListInternalAccountsResponseTest.kt @@ -0,0 +1,161 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.Currency +import com.grid.api.models.quotes.PaymentInstructions +import com.grid.api.models.sandbox.internalaccounts.InternalAccount +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PlatformListInternalAccountsResponseTest { + + @Test + fun create() { + val platformListInternalAccountsResponse = + PlatformListInternalAccountsResponse.builder() + .addData( + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .build() + + assertThat(platformListInternalAccountsResponse.data()) + .containsExactly( + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val platformListInternalAccountsResponse = + PlatformListInternalAccountsResponse.builder() + .addData( + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .build() + + val roundtrippedPlatformListInternalAccountsResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(platformListInternalAccountsResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedPlatformListInternalAccountsResponse) + .isEqualTo(platformListInternalAccountsResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParamsTest.kt new file mode 100644 index 00000000..b8e58030 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountCreateParamsTest.kt @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform.externalaccounts + +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountCreateParamsTest { + + @Test + fun create() { + ExternalAccountCreateParams.builder() + .externalAccountCreate( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + .build() + } + + @Test + fun body() { + val params = + ExternalAccountCreateParams.builder() + .externalAccountCreate( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ExternalAccountCreateParams.builder() + .externalAccountCreate( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .build() + ) + .routingNumber("123456789") + .build() + ) + .currency("USD") + .build() + ) + .build() + + val body = params._body() + + assertThat(body) + .isEqualTo( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .build() + ) + .routingNumber("123456789") + .build() + ) + .currency("USD") + .build() + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParamsTest.kt new file mode 100644 index 00000000..3cee998d --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListParamsTest.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform.externalaccounts + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountListParamsTest { + + @Test + fun create() { + ExternalAccountListParams.builder().currency("currency").build() + } + + @Test + fun queryParams() { + val params = ExternalAccountListParams.builder().currency("currency").build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().put("currency", "currency").build()) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = ExternalAccountListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponseTest.kt new file mode 100644 index 00000000..5b938575 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/platform/externalaccounts/ExternalAccountListResponseTest.kt @@ -0,0 +1,151 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.platform.externalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccount +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ExternalAccountListResponseTest { + + @Test + fun create() { + val externalAccountListResponse = + ExternalAccountListResponse.builder() + .addData( + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + ) + .build() + + assertThat(externalAccountListResponse.data()) + .containsExactly( + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val externalAccountListResponse = + ExternalAccountListResponse.builder() + .addData( + ExternalAccount.builder() + .id("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .status(ExternalAccount.Status.ACTIVE) + .customerId("Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7") + .defaultUmaDepositAccount(false) + .platformAccountId("acc_123456789") + .build() + ) + .build() + + val roundtrippedExternalAccountListResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(externalAccountListResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedExternalAccountListResponse).isEqualTo(externalAccountListResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseDestinationTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseDestinationTest.kt new file mode 100644 index 00000000..f92ae723 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseDestinationTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BaseDestinationTest { + + @Test + fun create() { + val baseDestination = BaseDestination.builder().build() + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val baseDestination = BaseDestination.builder().build() + + val roundtrippedBaseDestination = + jsonMapper.readValue( + jsonMapper.writeValueAsString(baseDestination), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBaseDestination).isEqualTo(baseDestination) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfoTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfoTest.kt new file mode 100644 index 00000000..f1ec827a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BasePaymentAccountInfoTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BasePaymentAccountInfoTest { + + @Test + fun create() { + val basePaymentAccountInfo = BasePaymentAccountInfo.builder().build() + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val basePaymentAccountInfo = BasePaymentAccountInfo.builder().build() + + val roundtrippedBasePaymentAccountInfo = + jsonMapper.readValue( + jsonMapper.writeValueAsString(basePaymentAccountInfo), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBasePaymentAccountInfo).isEqualTo(basePaymentAccountInfo) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseQuoteSourceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseQuoteSourceTest.kt new file mode 100644 index 00000000..fa42ac27 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/BaseQuoteSourceTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BaseQuoteSourceTest { + + @Test + fun create() { + val baseQuoteSource = BaseQuoteSource.builder().build() + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val baseQuoteSource = BaseQuoteSource.builder().build() + + val roundtrippedBaseQuoteSource = + jsonMapper.readValue( + jsonMapper.writeValueAsString(baseQuoteSource), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBaseQuoteSource).isEqualTo(baseQuoteSource) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/CurrencyTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/CurrencyTest.kt new file mode 100644 index 00000000..7e6238c0 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/CurrencyTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CurrencyTest { + + @Test + fun create() { + val currency = + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + + assertThat(currency.code()).isEqualTo("USD") + assertThat(currency.decimals()).isEqualTo(2L) + assertThat(currency.name()).isEqualTo("United States Dollar") + assertThat(currency.symbol()).isEqualTo("\$") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val currency = + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + + val roundtrippedCurrency = + jsonMapper.readValue( + jsonMapper.writeValueAsString(currency), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCurrency).isEqualTo(currency) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/OutgoingRateDetailsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/OutgoingRateDetailsTest.kt new file mode 100644 index 00000000..08dafd01 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/OutgoingRateDetailsTest.kt @@ -0,0 +1,53 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class OutgoingRateDetailsTest { + + @Test + fun create() { + val outgoingRateDetails = + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + + assertThat(outgoingRateDetails.counterpartyFixedFee()).isEqualTo(10L) + assertThat(outgoingRateDetails.counterpartyMultiplier()).isEqualTo(1.08) + assertThat(outgoingRateDetails.gridApiFixedFee()).isEqualTo(10L) + assertThat(outgoingRateDetails.gridApiMultiplier()).isEqualTo(0.925) + assertThat(outgoingRateDetails.gridApiVariableFeeAmount()).isEqualTo(30.0) + assertThat(outgoingRateDetails.gridApiVariableFeeRate()).isEqualTo(0.003) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val outgoingRateDetails = + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + + val roundtrippedOutgoingRateDetails = + jsonMapper.readValue( + jsonMapper.writeValueAsString(outgoingRateDetails), + jacksonTypeRef(), + ) + + assertThat(roundtrippedOutgoingRateDetails).isEqualTo(outgoingRateDetails) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/PaymentInstructionsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/PaymentInstructionsTest.kt new file mode 100644 index 00000000..aabc1aba --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/PaymentInstructionsTest.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PaymentInstructionsTest { + + @Test + fun create() { + val paymentInstructions = + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + + assertThat(paymentInstructions.accountOrWalletInfo()) + .isEqualTo( + PaymentInstructions.AccountOrWalletInfo.ofClabe( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + ) + assertThat(paymentInstructions.instructionsNotes()) + .isEqualTo( + "Please ensure the reference code is included in the payment memo/description field" + ) + assertThat(paymentInstructions.isPlatformAccount()).isEqualTo(true) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val paymentInstructions = + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + + val roundtrippedPaymentInstructions = + jsonMapper.readValue( + jsonMapper.writeValueAsString(paymentInstructions), + jacksonTypeRef(), + ) + + assertThat(roundtrippedPaymentInstructions).isEqualTo(paymentInstructions) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteCreateParamsTest.kt new file mode 100644 index 00000000..8b5c9a5b --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteCreateParamsTest.kt @@ -0,0 +1,132 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.grid.api.core.JsonValue +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class QuoteCreateParamsTest { + + @Test + fun create() { + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + } + + @Test + fun body() { + val params = + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + + val body = params._body() + + assertThat(body.destination()) + .isEqualTo( + QuoteDestinationOneOf.ofAccount( + QuoteDestinationOneOf.Account.builder() + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .build() + ) + ) + assertThat(body.lockedCurrencyAmount()).isEqualTo(10000L) + assertThat(body.lockedCurrencySide()) + .isEqualTo(QuoteCreateParams.LockedCurrencySide.SENDING) + assertThat(body.source()) + .isEqualTo( + QuoteSourceOneOf.ofAccount( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + ) + assertThat(body.description()) + .isEqualTo("Transfer between accounts, either internal or external.") + assertThat(body.immediatelyExecute()).isEqualTo(false) + assertThat(body.lookupId()).isEqualTo("Lookup:019542f5-b3e7-1d02-0000-000000000009") + assertThat(body.senderCustomerInfo()) + .isEqualTo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .accountSource("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + + val body = params._body() + + assertThat(body.destination()) + .isEqualTo( + QuoteDestinationOneOf.ofAccount( + QuoteDestinationOneOf.Account.builder() + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .build() + ) + ) + assertThat(body.lockedCurrencyAmount()).isEqualTo(10000L) + assertThat(body.lockedCurrencySide()) + .isEqualTo(QuoteCreateParams.LockedCurrencySide.SENDING) + assertThat(body.source()) + .isEqualTo( + QuoteSourceOneOf.ofAccount( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOfTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOfTest.kt new file mode 100644 index 00000000..8e04ec31 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteDestinationOneOfTest.kt @@ -0,0 +1,234 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class QuoteDestinationOneOfTest { + + @Test + fun ofAccount() { + val account = + QuoteDestinationOneOf.Account.builder() + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .build() + + val quoteDestinationOneOf = QuoteDestinationOneOf.ofAccount(account) + + assertThat(quoteDestinationOneOf.account()).isEqualTo(account) + assertThat(quoteDestinationOneOf.umaAddress()).isNull() + assertThat(quoteDestinationOneOf.externalAccountDetails()).isNull() + } + + @Test + fun ofAccountRoundtrip() { + val jsonMapper = jsonMapper() + val quoteDestinationOneOf = + QuoteDestinationOneOf.ofAccount( + QuoteDestinationOneOf.Account.builder() + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .build() + ) + + val roundtrippedQuoteDestinationOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteDestinationOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteDestinationOneOf).isEqualTo(quoteDestinationOneOf) + } + + @Test + fun ofUmaAddress() { + val umaAddress = + QuoteDestinationOneOf.UmaAddress.builder() + .destinationType(QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS) + .umaAddress("\$receiver@uma.domain.com") + .counterpartyInformation( + QuoteDestinationOneOf.UmaAddress.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .currency("EUR") + .build() + + val quoteDestinationOneOf = QuoteDestinationOneOf.ofUmaAddress(umaAddress) + + assertThat(quoteDestinationOneOf.account()).isNull() + assertThat(quoteDestinationOneOf.umaAddress()).isEqualTo(umaAddress) + assertThat(quoteDestinationOneOf.externalAccountDetails()).isNull() + } + + @Test + fun ofUmaAddressRoundtrip() { + val jsonMapper = jsonMapper() + val quoteDestinationOneOf = + QuoteDestinationOneOf.ofUmaAddress( + QuoteDestinationOneOf.UmaAddress.builder() + .destinationType(QuoteDestinationOneOf.UmaAddress.DestinationType.UMA_ADDRESS) + .umaAddress("\$receiver@uma.domain.com") + .counterpartyInformation( + QuoteDestinationOneOf.UmaAddress.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .currency("EUR") + .build() + ) + + val roundtrippedQuoteDestinationOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteDestinationOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteDestinationOneOf).isEqualTo(quoteDestinationOneOf) + } + + @Test + fun ofExternalAccountDetails() { + val externalAccountDetails = + QuoteDestinationOneOf.ExternalAccountDetails.builder() + .destinationType( + QuoteDestinationOneOf.ExternalAccountDetails.DestinationType + .EXTERNAL_ACCOUNT_DETAILS + ) + .externalAccountDetails( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + .build() + + val quoteDestinationOneOf = + QuoteDestinationOneOf.ofExternalAccountDetails(externalAccountDetails) + + assertThat(quoteDestinationOneOf.account()).isNull() + assertThat(quoteDestinationOneOf.umaAddress()).isNull() + assertThat(quoteDestinationOneOf.externalAccountDetails()).isEqualTo(externalAccountDetails) + } + + @Test + fun ofExternalAccountDetailsRoundtrip() { + val jsonMapper = jsonMapper() + val quoteDestinationOneOf = + QuoteDestinationOneOf.ofExternalAccountDetails( + QuoteDestinationOneOf.ExternalAccountDetails.builder() + .destinationType( + QuoteDestinationOneOf.ExternalAccountDetails.DestinationType + .EXTERNAL_ACCOUNT_DETAILS + ) + .externalAccountDetails( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("123456789") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("987654321") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + .build() + ) + + val roundtrippedQuoteDestinationOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteDestinationOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteDestinationOneOf).isEqualTo(quoteDestinationOneOf) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val quoteDestinationOneOf = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { quoteDestinationOneOf.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteExecuteParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteExecuteParamsTest.kt new file mode 100644 index 00000000..b8370221 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteExecuteParamsTest.kt @@ -0,0 +1,26 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class QuoteExecuteParamsTest { + + @Test + fun create() { + QuoteExecuteParams.builder().quoteId("Quote:019542f5-b3e7-1d02-0000-000000000001").build() + } + + @Test + fun pathParams() { + val params = + QuoteExecuteParams.builder() + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000001") + .build() + + assertThat(params._pathParam(0)).isEqualTo("Quote:019542f5-b3e7-1d02-0000-000000000001") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListPageResponseTest.kt new file mode 100644 index 00000000..caaa93c6 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListPageResponseTest.kt @@ -0,0 +1,302 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class QuoteListPageResponseTest { + + @Test + fun create() { + val quoteListPageResponse = + QuoteListPageResponse.builder() + .addData( + Quote.builder() + .createdAt(OffsetDateTime.parse("2025-10-03T12:00:00Z")) + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .exchangeRate(1.0) + .expiresAt(OffsetDateTime.parse("2025-10-03T12:05:00Z")) + .feesIncluded(10L) + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .receivingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .sendingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .status(Quote.Status.PENDING) + .totalReceivingAmount(1000L) + .totalSendingAmount(123010L) + .transactionId("Transaction:019542f5-b3e7-1d02-0000-000000000005") + .originalQuoteId("Quote:019542f5-b3e7-1d02-0000-000000000001") + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(quoteListPageResponse.data()) + .containsExactly( + Quote.builder() + .createdAt(OffsetDateTime.parse("2025-10-03T12:00:00Z")) + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .exchangeRate(1.0) + .expiresAt(OffsetDateTime.parse("2025-10-03T12:05:00Z")) + .feesIncluded(10L) + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .receivingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .sendingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .status(Quote.Status.PENDING) + .totalReceivingAmount(1000L) + .totalSendingAmount(123010L) + .transactionId("Transaction:019542f5-b3e7-1d02-0000-000000000005") + .originalQuoteId("Quote:019542f5-b3e7-1d02-0000-000000000001") + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .build() + ) + assertThat(quoteListPageResponse.hasMore()).isEqualTo(true) + assertThat(quoteListPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(quoteListPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val quoteListPageResponse = + QuoteListPageResponse.builder() + .addData( + Quote.builder() + .createdAt(OffsetDateTime.parse("2025-10-03T12:00:00Z")) + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .exchangeRate(1.0) + .expiresAt(OffsetDateTime.parse("2025-10-03T12:05:00Z")) + .feesIncluded(10L) + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .receivingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .sendingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .status(Quote.Status.PENDING) + .totalReceivingAmount(1000L) + .totalSendingAmount(123010L) + .transactionId("Transaction:019542f5-b3e7-1d02-0000-000000000005") + .originalQuoteId("Quote:019542f5-b3e7-1d02-0000-000000000001") + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe + .AccountType + .CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedQuoteListPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteListPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteListPageResponse).isEqualTo(quoteListPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListParamsTest.kt new file mode 100644 index 00000000..56204ad9 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteListParamsTest.kt @@ -0,0 +1,71 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.grid.api.core.http.QueryParams +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class QuoteListParamsTest { + + @Test + fun create() { + QuoteListParams.builder() + .createdAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .createdBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .cursor("cursor") + .customerId("customerId") + .limit(1L) + .receivingAccountId("receivingAccountId") + .receivingUmaAddress("receivingUmaAddress") + .sendingAccountId("sendingAccountId") + .sendingUmaAddress("sendingUmaAddress") + .status(QuoteListParams.Status.PENDING) + .build() + } + + @Test + fun queryParams() { + val params = + QuoteListParams.builder() + .createdAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .createdBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .cursor("cursor") + .customerId("customerId") + .limit(1L) + .receivingAccountId("receivingAccountId") + .receivingUmaAddress("receivingUmaAddress") + .sendingAccountId("sendingAccountId") + .sendingUmaAddress("sendingUmaAddress") + .status(QuoteListParams.Status.PENDING) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("createdAfter", "2019-12-27T18:11:19.117Z") + .put("createdBefore", "2019-12-27T18:11:19.117Z") + .put("cursor", "cursor") + .put("customerId", "customerId") + .put("limit", "1") + .put("receivingAccountId", "receivingAccountId") + .put("receivingUmaAddress", "receivingUmaAddress") + .put("sendingAccountId", "sendingAccountId") + .put("sendingUmaAddress", "sendingUmaAddress") + .put("status", "PENDING") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = QuoteListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteRetrieveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteRetrieveParamsTest.kt new file mode 100644 index 00000000..17a5f113 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteRetrieveParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class QuoteRetrieveParamsTest { + + @Test + fun create() { + QuoteRetrieveParams.builder().quoteId("quoteId").build() + } + + @Test + fun pathParams() { + val params = QuoteRetrieveParams.builder().quoteId("quoteId").build() + + assertThat(params._pathParam(0)).isEqualTo("quoteId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteSourceOneOfTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteSourceOneOfTest.kt new file mode 100644 index 00000000..8c4f7d31 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteSourceOneOfTest.kt @@ -0,0 +1,106 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class QuoteSourceOneOfTest { + + @Test + fun ofAccount() { + val account = + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + + val quoteSourceOneOf = QuoteSourceOneOf.ofAccount(account) + + assertThat(quoteSourceOneOf.account()).isEqualTo(account) + assertThat(quoteSourceOneOf.realtimeFunding()).isNull() + } + + @Test + fun ofAccountRoundtrip() { + val jsonMapper = jsonMapper() + val quoteSourceOneOf = + QuoteSourceOneOf.ofAccount( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + + val roundtrippedQuoteSourceOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteSourceOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteSourceOneOf).isEqualTo(quoteSourceOneOf) + } + + @Test + fun ofRealtimeFunding() { + val realtimeFunding = + QuoteSourceOneOf.RealtimeFunding.builder() + .currency("USD") + .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000009") + .build() + + val quoteSourceOneOf = QuoteSourceOneOf.ofRealtimeFunding(realtimeFunding) + + assertThat(quoteSourceOneOf.account()).isNull() + assertThat(quoteSourceOneOf.realtimeFunding()).isEqualTo(realtimeFunding) + } + + @Test + fun ofRealtimeFundingRoundtrip() { + val jsonMapper = jsonMapper() + val quoteSourceOneOf = + QuoteSourceOneOf.ofRealtimeFunding( + QuoteSourceOneOf.RealtimeFunding.builder() + .currency("USD") + .sourceType(QuoteSourceOneOf.RealtimeFunding.SourceType.REALTIME_FUNDING) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000009") + .build() + ) + + val roundtrippedQuoteSourceOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteSourceOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteSourceOneOf).isEqualTo(quoteSourceOneOf) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val quoteSourceOneOf = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { quoteSourceOneOf.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteTest.kt new file mode 100644 index 00000000..41fdb7fe --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/quotes/QuoteTest.kt @@ -0,0 +1,281 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.quotes + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class QuoteTest { + + @Test + fun create() { + val quote = + Quote.builder() + .createdAt(OffsetDateTime.parse("2025-10-03T12:00:00Z")) + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .exchangeRate(1.0) + .expiresAt(OffsetDateTime.parse("2025-10-03T12:05:00Z")) + .feesIncluded(10L) + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .receivingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .sendingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .status(Quote.Status.PENDING) + .totalReceivingAmount(1000L) + .totalSendingAmount(123010L) + .transactionId("Transaction:019542f5-b3e7-1d02-0000-000000000005") + .originalQuoteId("Quote:019542f5-b3e7-1d02-0000-000000000001") + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .build() + + assertThat(quote.createdAt()).isEqualTo(OffsetDateTime.parse("2025-10-03T12:00:00Z")) + assertThat(quote.destination()) + .isEqualTo( + QuoteDestinationOneOf.ofAccount( + QuoteDestinationOneOf.Account.builder() + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .build() + ) + ) + assertThat(quote.exchangeRate()).isEqualTo(1.0) + assertThat(quote.expiresAt()).isEqualTo(OffsetDateTime.parse("2025-10-03T12:05:00Z")) + assertThat(quote.feesIncluded()).isEqualTo(10L) + assertThat(quote.quoteId()).isEqualTo("Quote:019542f5-b3e7-1d02-0000-000000000006") + assertThat(quote.receivingCurrency()) + .isEqualTo( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + assertThat(quote.sendingCurrency()) + .isEqualTo( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + assertThat(quote.source()) + .isEqualTo( + QuoteSourceOneOf.ofAccount( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + ) + assertThat(quote.status()).isEqualTo(Quote.Status.PENDING) + assertThat(quote.totalReceivingAmount()).isEqualTo(1000L) + assertThat(quote.totalSendingAmount()).isEqualTo(123010L) + assertThat(quote.transactionId()) + .isEqualTo("Transaction:019542f5-b3e7-1d02-0000-000000000005") + assertThat(quote.originalQuoteId()).isEqualTo("Quote:019542f5-b3e7-1d02-0000-000000000001") + assertThat(quote.paymentInstructions()) + .containsExactly( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build(), + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build(), + ) + assertThat(quote.rateDetails()) + .isEqualTo( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val quote = + Quote.builder() + .createdAt(OffsetDateTime.parse("2025-10-03T12:00:00Z")) + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .exchangeRate(1.0) + .expiresAt(OffsetDateTime.parse("2025-10-03T12:05:00Z")) + .feesIncluded(10L) + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .receivingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .sendingCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .status(Quote.Status.PENDING) + .totalReceivingAmount(1000L) + .totalSendingAmount(123010L) + .transactionId("Transaction:019542f5-b3e7-1d02-0000-000000000005") + .originalQuoteId("Quote:019542f5-b3e7-1d02-0000-000000000001") + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .build() + + val roundtrippedQuote = + jsonMapper.readValue(jsonMapper.writeValueAsString(quote), jacksonTypeRef()) + + assertThat(roundtrippedQuote).isEqualTo(quote) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinitionTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinitionTest.kt new file mode 100644 index 00000000..e0bac2c9 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/CounterpartyFieldDefinitionTest.kt @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.config.CustomerInfoFieldName +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CounterpartyFieldDefinitionTest { + + @Test + fun create() { + val counterpartyFieldDefinition = + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + + assertThat(counterpartyFieldDefinition.mandatory()).isEqualTo(true) + assertThat(counterpartyFieldDefinition.name()).isEqualTo(CustomerInfoFieldName.FULL_NAME) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val counterpartyFieldDefinition = + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + + val roundtrippedCounterpartyFieldDefinition = + jsonMapper.readValue( + jsonMapper.writeValueAsString(counterpartyFieldDefinition), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCounterpartyFieldDefinition).isEqualTo(counterpartyFieldDefinition) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/LookupResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/LookupResponseTest.kt new file mode 100644 index 00000000..059c5ca3 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/LookupResponseTest.kt @@ -0,0 +1,106 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.config.CustomerInfoFieldName +import com.grid.api.models.quotes.Currency +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LookupResponseTest { + + @Test + fun create() { + val lookupResponse = + LookupResponse.builder() + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .addSupportedCurrency( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + .addRequiredPayerDataField( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + .build() + + assertThat(lookupResponse.lookupId()) + .isEqualTo("Lookup:019542f5-b3e7-1d02-0000-000000000009") + assertThat(lookupResponse.supportedCurrencies()) + .containsExactly( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + assertThat(lookupResponse.requiredPayerDataFields()) + .containsExactly( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val lookupResponse = + LookupResponse.builder() + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .addSupportedCurrency( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + .addRequiredPayerDataField( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + .build() + + val roundtrippedLookupResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(lookupResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedLookupResponse).isEqualTo(lookupResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParamsTest.kt new file mode 100644 index 00000000..b58f2307 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountParamsTest.kt @@ -0,0 +1,64 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ReceiverLookupExternalAccountParamsTest { + + @Test + fun create() { + ReceiverLookupExternalAccountParams.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + } + + @Test + fun pathParams() { + val params = + ReceiverLookupExternalAccountParams.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + + assertThat(params._pathParam(0)) + .isEqualTo("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun queryParams() { + val params = + ReceiverLookupExternalAccountParams.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("customerId", "customerId") + .put("senderUmaAddress", "senderUmaAddress") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = + ReceiverLookupExternalAccountParams.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponseTest.kt new file mode 100644 index 00000000..60c66b6a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupExternalAccountResponseTest.kt @@ -0,0 +1,111 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.config.CustomerInfoFieldName +import com.grid.api.models.quotes.Currency +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ReceiverLookupExternalAccountResponseTest { + + @Test + fun create() { + val receiverLookupExternalAccountResponse = + ReceiverLookupExternalAccountResponse.builder() + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .addSupportedCurrency( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + .addRequiredPayerDataField( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + + assertThat(receiverLookupExternalAccountResponse.lookupId()) + .isEqualTo("Lookup:019542f5-b3e7-1d02-0000-000000000009") + assertThat(receiverLookupExternalAccountResponse.supportedCurrencies()) + .containsExactly( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + assertThat(receiverLookupExternalAccountResponse.requiredPayerDataFields()) + .containsExactly( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + assertThat(receiverLookupExternalAccountResponse.accountId()) + .isEqualTo("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val receiverLookupExternalAccountResponse = + ReceiverLookupExternalAccountResponse.builder() + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .addSupportedCurrency( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + .addRequiredPayerDataField( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + + val roundtrippedReceiverLookupExternalAccountResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(receiverLookupExternalAccountResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedReceiverLookupExternalAccountResponse) + .isEqualTo(receiverLookupExternalAccountResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParamsTest.kt new file mode 100644 index 00000000..53982a8f --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaParamsTest.kt @@ -0,0 +1,59 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ReceiverLookupUmaParamsTest { + + @Test + fun create() { + ReceiverLookupUmaParams.builder() + .receiverUmaAddress("receiverUmaAddress") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + } + + @Test + fun pathParams() { + val params = + ReceiverLookupUmaParams.builder().receiverUmaAddress("receiverUmaAddress").build() + + assertThat(params._pathParam(0)).isEqualTo("receiverUmaAddress") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun queryParams() { + val params = + ReceiverLookupUmaParams.builder() + .receiverUmaAddress("receiverUmaAddress") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("customerId", "customerId") + .put("senderUmaAddress", "senderUmaAddress") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = + ReceiverLookupUmaParams.builder().receiverUmaAddress("receiverUmaAddress").build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponseTest.kt new file mode 100644 index 00000000..7aaa4f62 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/receiver/ReceiverLookupUmaResponseTest.kt @@ -0,0 +1,110 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.receiver + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.config.CustomerInfoFieldName +import com.grid.api.models.quotes.Currency +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ReceiverLookupUmaResponseTest { + + @Test + fun create() { + val receiverLookupUmaResponse = + ReceiverLookupUmaResponse.builder() + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .addSupportedCurrency( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + .addRequiredPayerDataField( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + .receiverUmaAddress("\$receiver@uma.domain") + .build() + + assertThat(receiverLookupUmaResponse.lookupId()) + .isEqualTo("Lookup:019542f5-b3e7-1d02-0000-000000000009") + assertThat(receiverLookupUmaResponse.supportedCurrencies()) + .containsExactly( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + assertThat(receiverLookupUmaResponse.requiredPayerDataFields()) + .containsExactly( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + assertThat(receiverLookupUmaResponse.receiverUmaAddress()) + .isEqualTo("\$receiver@uma.domain") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val receiverLookupUmaResponse = + ReceiverLookupUmaResponse.builder() + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .addSupportedCurrency( + LookupResponse.SupportedCurrency.builder() + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .estimatedExchangeRate(1.08) + .max(1000000L) + .min(1L) + .build() + ) + .addRequiredPayerDataField( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build() + ) + .receiverUmaAddress("\$receiver@uma.domain") + .build() + + val roundtrippedReceiverLookupUmaResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(receiverLookupUmaResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedReceiverLookupUmaResponse).isEqualTo(receiverLookupUmaResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParamsTest.kt new file mode 100644 index 00000000..3efec925 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsParamsTest.kt @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SandboxSendFundsParamsTest { + + @Test + fun create() { + SandboxSendFundsParams.builder() + .currencyCode("USD") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .currencyAmount(1000L) + .build() + } + + @Test + fun body() { + val params = + SandboxSendFundsParams.builder() + .currencyCode("USD") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .currencyAmount(1000L) + .build() + + val body = params._body() + + assertThat(body.currencyCode()).isEqualTo("USD") + assertThat(body.quoteId()).isEqualTo("Quote:019542f5-b3e7-1d02-0000-000000000006") + assertThat(body.currencyAmount()).isEqualTo(1000L) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + SandboxSendFundsParams.builder() + .currencyCode("USD") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .build() + + val body = params._body() + + assertThat(body.currencyCode()).isEqualTo("USD") + assertThat(body.quoteId()).isEqualTo("Quote:019542f5-b3e7-1d02-0000-000000000006") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponseTest.kt new file mode 100644 index 00000000..ce8b5441 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/SandboxSendFundsResponseTest.kt @@ -0,0 +1,399 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.Currency +import com.grid.api.models.quotes.OutgoingRateDetails +import com.grid.api.models.quotes.PaymentInstructions +import com.grid.api.models.transactions.TransactionSourceOneOf +import com.grid.api.models.transactions.TransactionStatus +import com.grid.api.models.transactions.TransactionType +import com.grid.api.models.transferin.Transaction +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SandboxSendFundsResponseTest { + + @Test + fun create() { + val sandboxSendFundsResponse = + SandboxSendFundsResponse.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .sentAmount( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .source( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + .exchangeRate(1.08) + .failureReason(SandboxSendFundsResponse.FailureReason.QUOTE_EXPIRED) + .fees(10L) + .originalTransactionId("Transaction:019542f5-b3e7-1d02-0000-000000000003") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .receivedAmount( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .refund( + SandboxSendFundsResponse.Refund.builder() + .initiatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .reference("UMA-Q12345-REFUND") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + ) + .build() + + assertThat(sandboxSendFundsResponse.id()) + .isEqualTo("Transaction:019542f5-b3e7-1d02-0000-000000000004") + assertThat(sandboxSendFundsResponse.customerId()) + .isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(sandboxSendFundsResponse.destination()) + .isEqualTo( + Transaction.Destination.ofAccount( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + ) + assertThat(sandboxSendFundsResponse.platformCustomerId()).isEqualTo("18d3e5f7b4a9c2") + assertThat(sandboxSendFundsResponse.status()).isEqualTo(TransactionStatus.CREATED) + assertThat(sandboxSendFundsResponse.type()).isEqualTo(TransactionType.INCOMING) + assertThat(sandboxSendFundsResponse.counterpartyInformation()) + .isEqualTo( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + assertThat(sandboxSendFundsResponse.createdAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + assertThat(sandboxSendFundsResponse.description()).isEqualTo("Payment for invoice #1234") + assertThat(sandboxSendFundsResponse.settledAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + assertThat(sandboxSendFundsResponse.updatedAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + assertThat(sandboxSendFundsResponse.paymentInstructions()) + .containsExactly( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build(), + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build(), + ) + assertThat(sandboxSendFundsResponse.sentAmount()) + .isEqualTo( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + assertThat(sandboxSendFundsResponse.source()) + .isEqualTo( + TransactionSourceOneOf.ofAccount( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + ) + assertThat(sandboxSendFundsResponse.exchangeRate()).isEqualTo(1.08) + assertThat(sandboxSendFundsResponse.failureReason()) + .isEqualTo(SandboxSendFundsResponse.FailureReason.QUOTE_EXPIRED) + assertThat(sandboxSendFundsResponse.fees()).isEqualTo(10L) + assertThat(sandboxSendFundsResponse.originalTransactionId()) + .isEqualTo("Transaction:019542f5-b3e7-1d02-0000-000000000003") + assertThat(sandboxSendFundsResponse.quoteId()) + .isEqualTo("Quote:019542f5-b3e7-1d02-0000-000000000006") + assertThat(sandboxSendFundsResponse.rateDetails()) + .isEqualTo( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + assertThat(sandboxSendFundsResponse.receivedAmount()) + .isEqualTo( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + assertThat(sandboxSendFundsResponse.refund()) + .isEqualTo( + SandboxSendFundsResponse.Refund.builder() + .initiatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .reference("UMA-Q12345-REFUND") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val sandboxSendFundsResponse = + SandboxSendFundsResponse.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .addPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .sentAmount( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .source( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + .exchangeRate(1.08) + .failureReason(SandboxSendFundsResponse.FailureReason.QUOTE_EXPIRED) + .fees(10L) + .originalTransactionId("Transaction:019542f5-b3e7-1d02-0000-000000000003") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .rateDetails( + OutgoingRateDetails.builder() + .counterpartyFixedFee(10L) + .counterpartyMultiplier(1.08) + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .receivedAmount( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .refund( + SandboxSendFundsResponse.Refund.builder() + .initiatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .reference("UMA-Q12345-REFUND") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + ) + .build() + + val roundtrippedSandboxSendFundsResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(sandboxSendFundsResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedSandboxSendFundsResponse).isEqualTo(sandboxSendFundsResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParamsTest.kt new file mode 100644 index 00000000..55bc4720 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountFundParamsTest.kt @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox.internalaccounts + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InternalAccountFundParamsTest { + + @Test + fun create() { + InternalAccountFundParams.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .amount(100000L) + .build() + } + + @Test + fun pathParams() { + val params = + InternalAccountFundParams.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .amount(100000L) + .build() + + assertThat(params._pathParam(0)) + .isEqualTo("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + InternalAccountFundParams.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .amount(100000L) + .build() + + val body = params._body() + + assertThat(body.amount()).isEqualTo(100000L) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountTest.kt new file mode 100644 index 00000000..273edd6f --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/internalaccounts/InternalAccountTest.kt @@ -0,0 +1,147 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox.internalaccounts + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.Currency +import com.grid.api.models.quotes.PaymentInstructions +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InternalAccountTest { + + @Test + fun create() { + val internalAccount = + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + + assertThat(internalAccount.id()) + .isEqualTo("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + assertThat(internalAccount.balance()) + .isEqualTo( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + assertThat(internalAccount.createdAt()) + .isEqualTo(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + assertThat(internalAccount.fundingPaymentInstructions()) + .containsExactly( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + assertThat(internalAccount.updatedAt()) + .isEqualTo(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + assertThat(internalAccount.customerId()) + .isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val internalAccount = + InternalAccount.builder() + .id("InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .balance( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .addFundingPaymentInstruction( + PaymentInstructions.builder() + .accountOrWalletInfo( + PaymentInstructions.AccountOrWalletInfo.Clabe.builder() + .accountType( + PaymentInstructions.AccountOrWalletInfo.Clabe.AccountType.CLABE + ) + .clabeNumber("123456789012345678") + .reference("UMA-Q12345-REF") + .build() + ) + .instructionsNotes( + "Please ensure the reference code is included in the payment memo/description field" + ) + .isPlatformAccount(true) + .build() + ) + .updatedAt(OffsetDateTime.parse("2025-10-03T12:30:00Z")) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + + val roundtrippedInternalAccount = + jsonMapper.readValue( + jsonMapper.writeValueAsString(internalAccount), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInternalAccount).isEqualTo(internalAccount) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParamsTest.kt new file mode 100644 index 00000000..a39a589d --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/sandbox/uma/UmaReceivePaymentParamsTest.kt @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.sandbox.uma + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UmaReceivePaymentParamsTest { + + @Test + fun create() { + UmaReceivePaymentParams.builder() + .receivingCurrencyAmount(1000L) + .receivingCurrencyCode("USD") + .senderUmaAddress("\$success.usd@sandbox.grid.uma.money") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .receiverUmaAddress("\$receiver@uma.domain") + .build() + } + + @Test + fun body() { + val params = + UmaReceivePaymentParams.builder() + .receivingCurrencyAmount(1000L) + .receivingCurrencyCode("USD") + .senderUmaAddress("\$success.usd@sandbox.grid.uma.money") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .receiverUmaAddress("\$receiver@uma.domain") + .build() + + val body = params._body() + + assertThat(body.receivingCurrencyAmount()).isEqualTo(1000L) + assertThat(body.receivingCurrencyCode()).isEqualTo("USD") + assertThat(body.senderUmaAddress()).isEqualTo("\$success.usd@sandbox.grid.uma.money") + assertThat(body.customerId()).isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(body.receiverUmaAddress()).isEqualTo("\$receiver@uma.domain") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + UmaReceivePaymentParams.builder() + .receivingCurrencyAmount(1000L) + .receivingCurrencyCode("USD") + .senderUmaAddress("\$success.usd@sandbox.grid.uma.money") + .build() + + val body = params._body() + + assertThat(body.receivingCurrencyAmount()).isEqualTo(1000L) + assertThat(body.receivingCurrencyCode()).isEqualTo("USD") + assertThat(body.senderUmaAddress()).isEqualTo("\$success.usd@sandbox.grid.uma.money") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/ApiTokenTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/ApiTokenTest.kt new file mode 100644 index 00000000..aef4464e --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/ApiTokenTest.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiTokenTest { + + @Test + fun create() { + val apiToken = + ApiToken.builder() + .id("Token:019542f5-b3e7-1d02-0000-000000000001") + .clientId("01947d2284054f890000e63bca4810df") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .name("Sandbox read-only token") + .addPermission(Permission.VIEW) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .clientSecret("ed0ad25881e234cc28fb2dec0a4fe64e4172") + .build() + + assertThat(apiToken.id()).isEqualTo("Token:019542f5-b3e7-1d02-0000-000000000001") + assertThat(apiToken.clientId()).isEqualTo("01947d2284054f890000e63bca4810df") + assertThat(apiToken.createdAt()).isEqualTo(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + assertThat(apiToken.name()).isEqualTo("Sandbox read-only token") + assertThat(apiToken.permissions()).containsExactly(Permission.VIEW) + assertThat(apiToken.updatedAt()).isEqualTo(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + assertThat(apiToken.clientSecret()).isEqualTo("ed0ad25881e234cc28fb2dec0a4fe64e4172") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val apiToken = + ApiToken.builder() + .id("Token:019542f5-b3e7-1d02-0000-000000000001") + .clientId("01947d2284054f890000e63bca4810df") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .name("Sandbox read-only token") + .addPermission(Permission.VIEW) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .clientSecret("ed0ad25881e234cc28fb2dec0a4fe64e4172") + .build() + + val roundtrippedApiToken = + jsonMapper.readValue( + jsonMapper.writeValueAsString(apiToken), + jacksonTypeRef(), + ) + + assertThat(roundtrippedApiToken).isEqualTo(apiToken) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenCreateParamsTest.kt new file mode 100644 index 00000000..c09a706a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenCreateParamsTest.kt @@ -0,0 +1,28 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TokenCreateParamsTest { + + @Test + fun create() { + TokenCreateParams.builder().name("Sandbox read-only").addPermission(Permission.VIEW).build() + } + + @Test + fun body() { + val params = + TokenCreateParams.builder() + .name("Sandbox read-only") + .addPermission(Permission.VIEW) + .build() + + val body = params._body() + + assertThat(body.name()).isEqualTo("Sandbox read-only") + assertThat(body.permissions()).containsExactly(Permission.VIEW) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenDeleteParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenDeleteParamsTest.kt new file mode 100644 index 00000000..f579293e --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenDeleteParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TokenDeleteParamsTest { + + @Test + fun create() { + TokenDeleteParams.builder().tokenId("tokenId").build() + } + + @Test + fun pathParams() { + val params = TokenDeleteParams.builder().tokenId("tokenId").build() + + assertThat(params._pathParam(0)).isEqualTo("tokenId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListPageResponseTest.kt new file mode 100644 index 00000000..7b7f3ef1 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListPageResponseTest.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TokenListPageResponseTest { + + @Test + fun create() { + val tokenListPageResponse = + TokenListPageResponse.builder() + .addData( + ApiToken.builder() + .id("Token:019542f5-b3e7-1d02-0000-000000000001") + .clientId("01947d2284054f890000e63bca4810df") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .name("Sandbox read-only token") + .addPermission(Permission.VIEW) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .clientSecret("ed0ad25881e234cc28fb2dec0a4fe64e4172") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(tokenListPageResponse.data()) + .containsExactly( + ApiToken.builder() + .id("Token:019542f5-b3e7-1d02-0000-000000000001") + .clientId("01947d2284054f890000e63bca4810df") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .name("Sandbox read-only token") + .addPermission(Permission.VIEW) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .clientSecret("ed0ad25881e234cc28fb2dec0a4fe64e4172") + .build() + ) + assertThat(tokenListPageResponse.hasMore()).isEqualTo(true) + assertThat(tokenListPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(tokenListPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val tokenListPageResponse = + TokenListPageResponse.builder() + .addData( + ApiToken.builder() + .id("Token:019542f5-b3e7-1d02-0000-000000000001") + .clientId("01947d2284054f890000e63bca4810df") + .createdAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .name("Sandbox read-only token") + .addPermission(Permission.VIEW) + .updatedAt(OffsetDateTime.parse("2025-07-21T17:32:28Z")) + .clientSecret("ed0ad25881e234cc28fb2dec0a4fe64e4172") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedTokenListPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(tokenListPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTokenListPageResponse).isEqualTo(tokenListPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListParamsTest.kt new file mode 100644 index 00000000..d01af50d --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenListParamsTest.kt @@ -0,0 +1,62 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import com.grid.api.core.http.QueryParams +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TokenListParamsTest { + + @Test + fun create() { + TokenListParams.builder() + .createdAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .createdBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .cursor("cursor") + .limit(1L) + .name("name") + .updatedAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .updatedBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + } + + @Test + fun queryParams() { + val params = + TokenListParams.builder() + .createdAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .createdBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .cursor("cursor") + .limit(1L) + .name("name") + .updatedAfter(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .updatedBefore(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("createdAfter", "2019-12-27T18:11:19.117Z") + .put("createdBefore", "2019-12-27T18:11:19.117Z") + .put("cursor", "cursor") + .put("limit", "1") + .put("name", "name") + .put("updatedAfter", "2019-12-27T18:11:19.117Z") + .put("updatedBefore", "2019-12-27T18:11:19.117Z") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = TokenListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenRetrieveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenRetrieveParamsTest.kt new file mode 100644 index 00000000..ae7bbfaf --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/tokens/TokenRetrieveParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.tokens + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TokenRetrieveParamsTest { + + @Test + fun create() { + TokenRetrieveParams.builder().tokenId("tokenId").build() + } + + @Test + fun pathParams() { + val params = TokenRetrieveParams.builder().tokenId("tokenId").build() + + assertThat(params._pathParam(0)).isEqualTo("tokenId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/BaseTransactionSourceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/BaseTransactionSourceTest.kt new file mode 100644 index 00000000..79d2e5fa --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/BaseTransactionSourceTest.kt @@ -0,0 +1,32 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BaseTransactionSourceTest { + + @Test + fun create() { + val baseTransactionSource = BaseTransactionSource.builder().currency("USD").build() + + assertThat(baseTransactionSource.currency()).isEqualTo("USD") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val baseTransactionSource = BaseTransactionSource.builder().currency("USD").build() + + val roundtrippedBaseTransactionSource = + jsonMapper.readValue( + jsonMapper.writeValueAsString(baseTransactionSource), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBaseTransactionSource).isEqualTo(baseTransactionSource) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/IncomingTransactionTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/IncomingTransactionTest.kt new file mode 100644 index 00000000..32672a2c --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/IncomingTransactionTest.kt @@ -0,0 +1,227 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.models.invitations.CurrencyAmount +import com.grid.api.models.quotes.Currency +import com.grid.api.models.transferin.Transaction +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class IncomingTransactionTest { + + @Test + fun create() { + val incomingTransaction = + IncomingTransaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .receivedAmount( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .failureReason(IncomingTransaction.FailureReason.LNURLP_FAILED) + .rateDetails( + IncomingTransaction.RateDetails.builder() + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .reconciliationInstructions( + IncomingTransaction.ReconciliationInstructions.builder() + .reference("UMA-Q12345-REF") + .build() + ) + .source( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + .build() + + assertThat(incomingTransaction.id()) + .isEqualTo("Transaction:019542f5-b3e7-1d02-0000-000000000004") + assertThat(incomingTransaction.customerId()) + .isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(incomingTransaction.destination()) + .isEqualTo( + Transaction.Destination.ofAccount( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + ) + assertThat(incomingTransaction.platformCustomerId()).isEqualTo("18d3e5f7b4a9c2") + assertThat(incomingTransaction.status()).isEqualTo(TransactionStatus.CREATED) + assertThat(incomingTransaction.type()).isEqualTo(TransactionType.INCOMING) + assertThat(incomingTransaction.counterpartyInformation()) + .isEqualTo( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + assertThat(incomingTransaction.createdAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + assertThat(incomingTransaction.description()).isEqualTo("Payment for invoice #1234") + assertThat(incomingTransaction.settledAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + assertThat(incomingTransaction.updatedAt()) + .isEqualTo(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + assertThat(incomingTransaction.receivedAmount()) + .isEqualTo( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + assertThat(incomingTransaction.failureReason()) + .isEqualTo(IncomingTransaction.FailureReason.LNURLP_FAILED) + assertThat(incomingTransaction.rateDetails()) + .isEqualTo( + IncomingTransaction.RateDetails.builder() + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + assertThat(incomingTransaction.reconciliationInstructions()) + .isEqualTo( + IncomingTransaction.ReconciliationInstructions.builder() + .reference("UMA-Q12345-REF") + .build() + ) + assertThat(incomingTransaction.source()) + .isEqualTo( + TransactionSourceOneOf.ofAccount( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val incomingTransaction = + IncomingTransaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .receivedAmount( + CurrencyAmount.builder() + .amount(12550L) + .currency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .build() + ) + .failureReason(IncomingTransaction.FailureReason.LNURLP_FAILED) + .rateDetails( + IncomingTransaction.RateDetails.builder() + .gridApiFixedFee(10L) + .gridApiMultiplier(0.925) + .gridApiVariableFeeAmount(30.0) + .gridApiVariableFeeRate(0.003) + .build() + ) + .reconciliationInstructions( + IncomingTransaction.ReconciliationInstructions.builder() + .reference("UMA-Q12345-REF") + .build() + ) + .source( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + .build() + + val roundtrippedIncomingTransaction = + jsonMapper.readValue( + jsonMapper.writeValueAsString(incomingTransaction), + jacksonTypeRef(), + ) + + assertThat(roundtrippedIncomingTransaction).isEqualTo(incomingTransaction) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionApproveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionApproveParamsTest.kt new file mode 100644 index 00000000..675b1544 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionApproveParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.grid.api.core.JsonValue +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionApproveParamsTest { + + @Test + fun create() { + TransactionApproveParams.builder() + .transactionId("transactionId") + .receiverCustomerInfo( + TransactionApproveParams.ReceiverCustomerInfo.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + } + + @Test + fun pathParams() { + val params = TransactionApproveParams.builder().transactionId("transactionId").build() + + assertThat(params._pathParam(0)).isEqualTo("transactionId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + TransactionApproveParams.builder() + .transactionId("transactionId") + .receiverCustomerInfo( + TransactionApproveParams.ReceiverCustomerInfo.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + + val body = params._body() + + assertThat(body.receiverCustomerInfo()) + .isEqualTo( + TransactionApproveParams.ReceiverCustomerInfo.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = TransactionApproveParams.builder().transactionId("transactionId").build() + + val body = params._body() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListPageResponseTest.kt new file mode 100644 index 00000000..309eae18 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListPageResponseTest.kt @@ -0,0 +1,135 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.models.transferin.Transaction +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionListPageResponseTest { + + @Test + fun create() { + val transactionListPageResponse = + TransactionListPageResponse.builder() + .addData( + Transaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType( + Transaction.Destination.Account.DestinationType.ACCOUNT + ) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(transactionListPageResponse.data()) + .containsExactly( + Transaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType( + Transaction.Destination.Account.DestinationType.ACCOUNT + ) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + ) + assertThat(transactionListPageResponse.hasMore()).isEqualTo(true) + assertThat(transactionListPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(transactionListPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transactionListPageResponse = + TransactionListPageResponse.builder() + .addData( + Transaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType( + Transaction.Destination.Account.DestinationType.ACCOUNT + ) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedTransactionListPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transactionListPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTransactionListPageResponse).isEqualTo(transactionListPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListParamsTest.kt new file mode 100644 index 00000000..1266d913 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionListParamsTest.kt @@ -0,0 +1,77 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.grid.api.core.http.QueryParams +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionListParamsTest { + + @Test + fun create() { + TransactionListParams.builder() + .cursor("cursor") + .customerId("customerId") + .endDate(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .limit(1L) + .platformCustomerId("platformCustomerId") + .receiverAccountIdentifier("receiverAccountIdentifier") + .reference("reference") + .senderAccountIdentifier("senderAccountIdentifier") + .sortOrder(TransactionListParams.SortOrder.ASC) + .startDate(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .build() + } + + @Test + fun queryParams() { + val params = + TransactionListParams.builder() + .cursor("cursor") + .customerId("customerId") + .endDate(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .limit(1L) + .platformCustomerId("platformCustomerId") + .receiverAccountIdentifier("receiverAccountIdentifier") + .reference("reference") + .senderAccountIdentifier("senderAccountIdentifier") + .sortOrder(TransactionListParams.SortOrder.ASC) + .startDate(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("cursor", "cursor") + .put("customerId", "customerId") + .put("endDate", "2019-12-27T18:11:19.117Z") + .put("limit", "1") + .put("platformCustomerId", "platformCustomerId") + .put("receiverAccountIdentifier", "receiverAccountIdentifier") + .put("reference", "reference") + .put("senderAccountIdentifier", "senderAccountIdentifier") + .put("sortOrder", "asc") + .put("startDate", "2019-12-27T18:11:19.117Z") + .put("status", "CREATED") + .put("type", "INCOMING") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = TransactionListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRejectParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRejectParamsTest.kt new file mode 100644 index 00000000..bebdf809 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRejectParamsTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionRejectParamsTest { + + @Test + fun create() { + TransactionRejectParams.builder() + .transactionId("transactionId") + .reason("RESTRICTED_JURISDICTION") + .build() + } + + @Test + fun pathParams() { + val params = TransactionRejectParams.builder().transactionId("transactionId").build() + + assertThat(params._pathParam(0)).isEqualTo("transactionId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + TransactionRejectParams.builder() + .transactionId("transactionId") + .reason("RESTRICTED_JURISDICTION") + .build() + + val body = params._body() + + assertThat(body.reason()).isEqualTo("RESTRICTED_JURISDICTION") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = TransactionRejectParams.builder().transactionId("transactionId").build() + + val body = params._body() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRetrieveParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRetrieveParamsTest.kt new file mode 100644 index 00000000..6d6aa4e8 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionRetrieveParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionRetrieveParamsTest { + + @Test + fun create() { + TransactionRetrieveParams.builder().transactionId("transactionId").build() + } + + @Test + fun pathParams() { + val params = TransactionRetrieveParams.builder().transactionId("transactionId").build() + + assertThat(params._pathParam(0)).isEqualTo("transactionId") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionSourceOneOfTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionSourceOneOfTest.kt new file mode 100644 index 00000000..47112dab --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transactions/TransactionSourceOneOfTest.kt @@ -0,0 +1,106 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transactions + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.errors.GridInvalidDataException +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class TransactionSourceOneOfTest { + + @Test + fun ofAccount() { + val account = + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + + val transactionSourceOneOf = TransactionSourceOneOf.ofAccount(account) + + assertThat(transactionSourceOneOf.account()).isEqualTo(account) + assertThat(transactionSourceOneOf.umaAddress()).isNull() + } + + @Test + fun ofAccountRoundtrip() { + val jsonMapper = jsonMapper() + val transactionSourceOneOf = + TransactionSourceOneOf.ofAccount( + TransactionSourceOneOf.Account.builder() + .currency("USD") + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(TransactionSourceOneOf.Account.SourceType.ACCOUNT) + .build() + ) + + val roundtrippedTransactionSourceOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transactionSourceOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTransactionSourceOneOf).isEqualTo(transactionSourceOneOf) + } + + @Test + fun ofUmaAddress() { + val umaAddress = + TransactionSourceOneOf.UmaAddress.builder() + .currency("USD") + .sourceType(TransactionSourceOneOf.UmaAddress.SourceType.UMA_ADDRESS) + .umaAddress("\$sender@uma.domain.com") + .build() + + val transactionSourceOneOf = TransactionSourceOneOf.ofUmaAddress(umaAddress) + + assertThat(transactionSourceOneOf.account()).isNull() + assertThat(transactionSourceOneOf.umaAddress()).isEqualTo(umaAddress) + } + + @Test + fun ofUmaAddressRoundtrip() { + val jsonMapper = jsonMapper() + val transactionSourceOneOf = + TransactionSourceOneOf.ofUmaAddress( + TransactionSourceOneOf.UmaAddress.builder() + .currency("USD") + .sourceType(TransactionSourceOneOf.UmaAddress.SourceType.UMA_ADDRESS) + .umaAddress("\$sender@uma.domain.com") + .build() + ) + + val roundtrippedTransactionSourceOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transactionSourceOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTransactionSourceOneOf).isEqualTo(transactionSourceOneOf) + } + + enum class IncompatibleJsonShapeTestCase(val value: JsonValue) { + BOOLEAN(JsonValue.from(false)), + STRING(JsonValue.from("invalid")), + INTEGER(JsonValue.from(-1)), + FLOAT(JsonValue.from(3.14)), + ARRAY(JsonValue.from(listOf("invalid", "array"))), + } + + @ParameterizedTest + @EnumSource + fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) { + val transactionSourceOneOf = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { transactionSourceOneOf.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/BaseTransactionDestinationTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/BaseTransactionDestinationTest.kt new file mode 100644 index 00000000..ede9da2c --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/BaseTransactionDestinationTest.kt @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferin + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BaseTransactionDestinationTest { + + @Test + fun create() { + val baseTransactionDestination = + BaseTransactionDestination.builder().currency("EUR").build() + + assertThat(baseTransactionDestination.currency()).isEqualTo("EUR") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val baseTransactionDestination = + BaseTransactionDestination.builder().currency("EUR").build() + + val roundtrippedBaseTransactionDestination = + jsonMapper.readValue( + jsonMapper.writeValueAsString(baseTransactionDestination), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBaseTransactionDestination).isEqualTo(baseTransactionDestination) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransactionTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransactionTest.kt new file mode 100644 index 00000000..1c978428 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransactionTest.kt @@ -0,0 +1,113 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferin + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.JsonValue +import com.grid.api.core.jsonMapper +import com.grid.api.models.transactions.TransactionStatus +import com.grid.api.models.transactions.TransactionType +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionTest { + + @Test + fun create() { + val transaction = + Transaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + + assertThat(transaction.id()).isEqualTo("Transaction:019542f5-b3e7-1d02-0000-000000000004") + assertThat(transaction.customerId()) + .isEqualTo("Customer:019542f5-b3e7-1d02-0000-000000000001") + assertThat(transaction.destination()) + .isEqualTo( + Transaction.Destination.ofAccount( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + ) + assertThat(transaction.platformCustomerId()).isEqualTo("18d3e5f7b4a9c2") + assertThat(transaction.status()).isEqualTo(TransactionStatus.CREATED) + assertThat(transaction.type()).isEqualTo(TransactionType.INCOMING) + assertThat(transaction.counterpartyInformation()) + .isEqualTo( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + assertThat(transaction.createdAt()).isEqualTo(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + assertThat(transaction.description()).isEqualTo("Payment for invoice #1234") + assertThat(transaction.settledAt()).isEqualTo(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + assertThat(transaction.updatedAt()).isEqualTo(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transaction = + Transaction.builder() + .id("Transaction:019542f5-b3e7-1d02-0000-000000000004") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .destination( + Transaction.Destination.Account.builder() + .currency("EUR") + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(Transaction.Destination.Account.DestinationType.ACCOUNT) + .build() + ) + .platformCustomerId("18d3e5f7b4a9c2") + .status(TransactionStatus.CREATED) + .type(TransactionType.INCOMING) + .counterpartyInformation( + Transaction.CounterpartyInformation.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("BIRTH_DATE", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .createdAt(OffsetDateTime.parse("2025-08-15T14:25:18Z")) + .description("Payment for invoice #1234") + .settledAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .updatedAt(OffsetDateTime.parse("2025-08-15T14:30:00Z")) + .build() + + val roundtrippedTransaction = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transaction), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTransaction).isEqualTo(transaction) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransferInCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransferInCreateParamsTest.kt new file mode 100644 index 00000000..1ad0006a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferin/TransferInCreateParamsTest.kt @@ -0,0 +1,144 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferin + +import com.grid.api.core.http.Headers +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransferInCreateParamsTest { + + @Test + fun create() { + TransferInCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .amount(12550L) + .build() + } + + @Test + fun headers() { + val params = + TransferInCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .amount(12550L) + .build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("Idempotency-Key", "550e8400-e29b-41d4-a716-446655440000") + .build() + ) + } + + @Test + fun headersWithoutOptionalFields() { + val params = + TransferInCreateParams.builder() + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().build()) + } + + @Test + fun body() { + val params = + TransferInCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .amount(12550L) + .build() + + val body = params._body() + + assertThat(body.destination()) + .isEqualTo( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + assertThat(body.source()) + .isEqualTo( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + assertThat(body.amount()).isEqualTo(12550L) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + TransferInCreateParams.builder() + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .build() + + val body = params._body() + + assertThat(body.destination()) + .isEqualTo( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + assertThat(body.source()) + .isEqualTo( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferout/TransferOutCreateParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferout/TransferOutCreateParamsTest.kt new file mode 100644 index 00000000..df8ca3c8 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/transferout/TransferOutCreateParamsTest.kt @@ -0,0 +1,144 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.transferout + +import com.grid.api.core.http.Headers +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransferOutCreateParamsTest { + + @Test + fun create() { + TransferOutCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .amount(12550L) + .build() + } + + @Test + fun headers() { + val params = + TransferOutCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .amount(12550L) + .build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo( + Headers.builder() + .put("Idempotency-Key", "550e8400-e29b-41d4-a716-446655440000") + .build() + ) + } + + @Test + fun headersWithoutOptionalFields() { + val params = + TransferOutCreateParams.builder() + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .build() + + val headers = params._headers() + + assertThat(headers).isEqualTo(Headers.builder().build()) + } + + @Test + fun body() { + val params = + TransferOutCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .amount(12550L) + .build() + + val body = params._body() + + assertThat(body.destination()) + .isEqualTo( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + assertThat(body.source()) + .isEqualTo( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + assertThat(body.amount()).isEqualTo(12550L) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + TransferOutCreateParams.builder() + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .build() + + val body = params._body() + + assertThat(body.destination()) + .isEqualTo( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + assertThat(body.source()) + .isEqualTo( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponseTest.kt new file mode 100644 index 00000000..a267ce72 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListPageResponseTest.kt @@ -0,0 +1,100 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.quotes.Currency +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UmaProviderListPageResponseTest { + + @Test + fun create() { + val umaProviderListPageResponse = + UmaProviderListPageResponse.builder() + .addData( + UmaProviderListResponse.builder() + .allowListStatus(true) + .domain("uma.me") + .lei("5493001KJTIIGC8Y1R12") + .logoUrl("https://uma.me/logo.png") + .name("Lightspark Group") + .addSupportedCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .addSupportedRegion("US") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + assertThat(umaProviderListPageResponse.data()) + .containsExactly( + UmaProviderListResponse.builder() + .allowListStatus(true) + .domain("uma.me") + .lei("5493001KJTIIGC8Y1R12") + .logoUrl("https://uma.me/logo.png") + .name("Lightspark Group") + .addSupportedCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .addSupportedRegion("US") + .build() + ) + assertThat(umaProviderListPageResponse.hasMore()).isEqualTo(true) + assertThat(umaProviderListPageResponse.nextCursor()).isEqualTo("nextCursor") + assertThat(umaProviderListPageResponse.totalCount()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val umaProviderListPageResponse = + UmaProviderListPageResponse.builder() + .addData( + UmaProviderListResponse.builder() + .allowListStatus(true) + .domain("uma.me") + .lei("5493001KJTIIGC8Y1R12") + .logoUrl("https://uma.me/logo.png") + .name("Lightspark Group") + .addSupportedCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .addSupportedRegion("US") + .build() + ) + .hasMore(true) + .nextCursor("nextCursor") + .totalCount(0L) + .build() + + val roundtrippedUmaProviderListPageResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(umaProviderListPageResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUmaProviderListPageResponse).isEqualTo(umaProviderListPageResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListParamsTest.kt new file mode 100644 index 00000000..d520da6a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListParamsTest.kt @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.grid.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UmaProviderListParamsTest { + + @Test + fun create() { + UmaProviderListParams.builder() + .countryCode("US") + .currencyCode("USD") + .cursor("cursor") + .hasBlockedProviders(true) + .limit(1L) + .sortOrder(UmaProviderListParams.SortOrder.ASC) + .build() + } + + @Test + fun queryParams() { + val params = + UmaProviderListParams.builder() + .countryCode("US") + .currencyCode("USD") + .cursor("cursor") + .hasBlockedProviders(true) + .limit(1L) + .sortOrder(UmaProviderListParams.SortOrder.ASC) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("countryCode", "US") + .put("currencyCode", "USD") + .put("cursor", "cursor") + .put("hasBlockedProviders", "true") + .put("limit", "1") + .put("sortOrder", "asc") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = UmaProviderListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponseTest.kt new file mode 100644 index 00000000..39460298 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/umaproviders/UmaProviderListResponseTest.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.umaproviders + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import com.grid.api.models.quotes.Currency +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UmaProviderListResponseTest { + + @Test + fun create() { + val umaProviderListResponse = + UmaProviderListResponse.builder() + .allowListStatus(true) + .domain("uma.me") + .lei("5493001KJTIIGC8Y1R12") + .logoUrl("https://uma.me/logo.png") + .name("Lightspark Group") + .addSupportedCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .addSupportedRegion("US") + .build() + + assertThat(umaProviderListResponse.allowListStatus()).isEqualTo(true) + assertThat(umaProviderListResponse.domain()).isEqualTo("uma.me") + assertThat(umaProviderListResponse.lei()).isEqualTo("5493001KJTIIGC8Y1R12") + assertThat(umaProviderListResponse.logoUrl()).isEqualTo("https://uma.me/logo.png") + assertThat(umaProviderListResponse.name()).isEqualTo("Lightspark Group") + assertThat(umaProviderListResponse.supportedCurrencies()) + .containsExactly( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + assertThat(umaProviderListResponse.supportedRegions()).containsExactly("US") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val umaProviderListResponse = + UmaProviderListResponse.builder() + .allowListStatus(true) + .domain("uma.me") + .lei("5493001KJTIIGC8Y1R12") + .logoUrl("https://uma.me/logo.png") + .name("Lightspark Group") + .addSupportedCurrency( + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + ) + .addSupportedRegion("US") + .build() + + val roundtrippedUmaProviderListResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(umaProviderListResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUmaProviderListResponse).isEqualTo(umaProviderListResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestParamsTest.kt new file mode 100644 index 00000000..dedfcbbb --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestParamsTest.kt @@ -0,0 +1,13 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.webhooks + +import org.junit.jupiter.api.Test + +internal class WebhookSendTestParamsTest { + + @Test + fun create() { + WebhookSendTestParams.builder().build() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponseTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponseTest.kt new file mode 100644 index 00000000..2b1bc012 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/models/webhooks/WebhookSendTestResponseTest.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.models.webhooks + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class WebhookSendTestResponseTest { + + @Test + fun create() { + val webhookSendTestResponse = + WebhookSendTestResponse.builder() + .responseStatus(200L) + .responseBody("response_body") + .url("https://api.mycompany.com/webhooks/uma") + .build() + + assertThat(webhookSendTestResponse.responseStatus()).isEqualTo(200L) + assertThat(webhookSendTestResponse.responseBody()).isEqualTo("response_body") + assertThat(webhookSendTestResponse.url()) + .isEqualTo("https://api.mycompany.com/webhooks/uma") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val webhookSendTestResponse = + WebhookSendTestResponse.builder() + .responseStatus(200L) + .responseBody("response_body") + .url("https://api.mycompany.com/webhooks/uma") + .build() + + val roundtrippedWebhookSendTestResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(webhookSendTestResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedWebhookSendTestResponse).isEqualTo(webhookSendTestResponse) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/ErrorHandlingTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/ErrorHandlingTest.kt new file mode 100644 index 00000000..66952883 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/ErrorHandlingTest.kt @@ -0,0 +1,777 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services + +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.status +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.core.JsonValue +import com.grid.api.core.http.Headers +import com.grid.api.core.jsonMapper +import com.grid.api.errors.BadRequestException +import com.grid.api.errors.GridException +import com.grid.api.errors.InternalServerException +import com.grid.api.errors.NotFoundException +import com.grid.api.errors.PermissionDeniedException +import com.grid.api.errors.RateLimitException +import com.grid.api.errors.UnauthorizedException +import com.grid.api.errors.UnexpectedStatusCodeException +import com.grid.api.errors.UnprocessableEntityException +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteSourceOneOf +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.entry +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class ErrorHandlingTest { + + companion object { + + private val ERROR_JSON: JsonValue = JsonValue.from(mapOf("errorProperty" to "42")) + + private val ERROR_JSON_BYTES: ByteArray = jsonMapper().writeValueAsBytes(ERROR_JSON) + + private const val HEADER_NAME: String = "Error-Header" + + private const val HEADER_VALUE: String = "42" + + private const val NOT_JSON: String = "Not JSON" + } + + private lateinit var client: GridClient + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + client = + GridOkHttpClient.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .username("My Username") + .password("My Password") + .build() + } + + @Test + fun quotesCreate400() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(400).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(400) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate400WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(400).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(400) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate401() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(401).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(401) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate401WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(401).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(401) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate403() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(403).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(403) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate403WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(403).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(403) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate404() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(404).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(404) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate404WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(404).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(404) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate422() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(422).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(422) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate422WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(422).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(422) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate429() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(429).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(429) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate429WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(429).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(429) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate500() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(500).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(500) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate500WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(500).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(500) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate999() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn( + status(999).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(999) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreate999WithRawResponse() { + val quoteService = client.quotes().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(999).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e.statusCode()).isEqualTo(999) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun quotesCreateInvalidJsonBody() { + val quoteService = client.quotes() + stubFor( + post(anyUrl()) + .willReturn(status(200).withHeader(HEADER_NAME, HEADER_VALUE).withBody(NOT_JSON)) + ) + + val e = + assertThrows { + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + } + + assertThat(e).hasMessage("Error reading response") + } + + private fun Headers.toMap(): Map> = + mutableMapOf>().also { map -> + names().forEach { map[it] = values(it) } + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/ServiceParamsTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/ServiceParamsTest.kt new file mode 100644 index 00000000..617f4df1 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/ServiceParamsTest.kt @@ -0,0 +1,81 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services + +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.verify +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.grid.api.client.GridClient +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.core.JsonValue +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteSourceOneOf +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class ServiceParamsTest { + + private lateinit var client: GridClient + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + client = + GridOkHttpClient.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .username("My Username") + .password("My Password") + .build() + } + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val quoteService = client.quotes() + stubFor(post(anyUrl()).willReturn(ok("{}"))) + + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .putAdditionalHeader("Secret-Header", "42") + .putAdditionalQueryParam("secret_query_param", "42") + .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) + .build() + ) + + verify( + postRequestedFor(anyUrl()) + .withHeader("Secret-Header", equalTo("42")) + .withQueryParam("secret_query_param", equalTo("42")) + .withRequestBody(matchingJsonPath("$.secretProperty", equalTo("42"))) + ) + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ConfigServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ConfigServiceAsyncTest.kt new file mode 100644 index 00000000..a31e687f --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ConfigServiceAsyncTest.kt @@ -0,0 +1,89 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.config.ConfigUpdateParams +import com.grid.api.models.config.CustomerInfoFieldName +import com.grid.api.models.config.PlatformCurrencyConfig +import com.grid.api.models.receiver.CounterpartyFieldDefinition +import com.grid.api.models.transactions.TransactionType +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ConfigServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun retrieve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val configServiceAsync = client.config() + + val platformConfig = configServiceAsync.retrieve() + + platformConfig.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun update() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val configServiceAsync = client.config() + + val platformConfig = + configServiceAsync.update( + ConfigUpdateParams.builder() + .addSupportedCurrency( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.FULL_NAME + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + .umaDomain("mycompany.com") + .webhookEndpoint("https://api.mycompany.com/webhooks/uma") + .build() + ) + + platformConfig.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/CustomerServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/CustomerServiceAsyncTest.kt new file mode 100644 index 00000000..1ec0a9e8 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/CustomerServiceAsyncTest.kt @@ -0,0 +1,191 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.customers.CustomerCreateParams +import com.grid.api.models.customers.CustomerGetKycLinkParams +import com.grid.api.models.customers.CustomerUpdateParams +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class CustomerServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val customerOneOf = + customerServiceAsync.create( + CustomerCreateParams.builder() + .createCustomerRequest( + CustomerCreateParams.CreateCustomerRequest.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerCreateParams.CreateCustomerRequest.Individual.Address + .builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + .build() + ) + + customerOneOf.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun retrieve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val customer = customerServiceAsync.retrieve("customerId") + + customer.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun update() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val customer = + customerServiceAsync.update( + CustomerUpdateParams.builder() + .customerId("customerId") + .updateCustomerRequest( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerUpdateParams.UpdateCustomerRequest.Individual.Address + .builder() + .country("US") + .line1("456 Market St") + .postalCode("94103") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1985-06-15")) + .fullName("John Smith") + .nationality("US") + .build() + ) + .build() + ) + + customer.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val page = customerServiceAsync.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun delete() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val customer = customerServiceAsync.delete("customerId") + + customer.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun getKycLink() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val response = + customerServiceAsync.getKycLink( + CustomerGetKycLinkParams.builder() + .platformCustomerId("platformCustomerId") + .redirectUri("redirectUri") + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun listInternalAccounts() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerServiceAsync = client.customers() + + val page = customerServiceAsync.listInternalAccounts() + + page.response().validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncTest.kt new file mode 100644 index 00000000..1acd0265 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ExchangeRateServiceAsyncTest.kt @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.exchangerates.ExchangeRateListParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ExchangeRateServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val exchangeRateServiceAsync = client.exchangeRates() + + val exchangeRates = + exchangeRateServiceAsync.list( + ExchangeRateListParams.builder() + .addDestinationCurrency("string") + .sendingAmount(0L) + .sourceCurrency("sourceCurrency") + .build() + ) + + exchangeRates.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/InvitationServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/InvitationServiceAsyncTest.kt new file mode 100644 index 00000000..c8855284 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/InvitationServiceAsyncTest.kt @@ -0,0 +1,94 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.invitations.InvitationClaimParams +import com.grid.api.models.invitations.InvitationCreateParams +import java.time.OffsetDateTime +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class InvitationServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationServiceAsync = client.invitations() + + val umaInvitation = + invitationServiceAsync.create( + InvitationCreateParams.builder() + .inviterUma("\$inviter@uma.domain") + .amountToSend(12550L) + .expiresAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .firstName("Alice") + .build() + ) + + umaInvitation.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun retrieve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationServiceAsync = client.invitations() + + val umaInvitation = invitationServiceAsync.retrieve("invitationCode") + + umaInvitation.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun cancel() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationServiceAsync = client.invitations() + + val umaInvitation = invitationServiceAsync.cancel("invitationCode") + + umaInvitation.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun claim() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationServiceAsync = client.invitations() + + val umaInvitation = + invitationServiceAsync.claim( + InvitationClaimParams.builder() + .invitationCode("invitationCode") + .inviteeUma("\$invitee@uma.domain") + .build() + ) + + umaInvitation.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlaidServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlaidServiceAsyncTest.kt new file mode 100644 index 00000000..3f688845 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlaidServiceAsyncTest.kt @@ -0,0 +1,59 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.plaid.PlaidCreateLinkTokenParams +import com.grid.api.models.plaid.PlaidSubmitPublicTokenParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class PlaidServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun createLinkToken() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val plaidServiceAsync = client.plaid() + + val response = + plaidServiceAsync.createLinkToken( + PlaidCreateLinkTokenParams.builder() + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun submitPublicToken() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val plaidServiceAsync = client.plaid() + + val externalAccount = + plaidServiceAsync.submitPublicToken( + PlaidSubmitPublicTokenParams.builder() + .plaidLinkToken("link-sandbox-abc123xyz-1234-5678") + .publicToken("public-sandbox-12345678-1234-1234-1234-123456789012") + .accountId("plaid_account_id_123") + .build() + ) + + externalAccount.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlatformServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlatformServiceAsyncTest.kt new file mode 100644 index 00000000..3cb600ed --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/PlatformServiceAsyncTest.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.platform.PlatformListInternalAccountsParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class PlatformServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun listInternalAccounts() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val platformServiceAsync = client.platform() + + val response = + platformServiceAsync.listInternalAccounts( + PlatformListInternalAccountsParams.builder().currency("currency").build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/QuoteServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/QuoteServiceAsyncTest.kt new file mode 100644 index 00000000..c394ffbf --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/QuoteServiceAsyncTest.kt @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.core.JsonValue +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteSourceOneOf +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class QuoteServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteServiceAsync = client.quotes() + + val quote = + quoteServiceAsync.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + + quote.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun retrieve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteServiceAsync = client.quotes() + + val quote = quoteServiceAsync.retrieve("quoteId") + + quote.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteServiceAsync = client.quotes() + + val page = quoteServiceAsync.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun execute() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteServiceAsync = client.quotes() + + val quote = quoteServiceAsync.execute("Quote:019542f5-b3e7-1d02-0000-000000000001") + + quote.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ReceiverServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ReceiverServiceAsyncTest.kt new file mode 100644 index 00000000..c04033bf --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/ReceiverServiceAsyncTest.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.receiver.ReceiverLookupExternalAccountParams +import com.grid.api.models.receiver.ReceiverLookupUmaParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ReceiverServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun lookupExternalAccount() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val receiverServiceAsync = client.receiver() + + val response = + receiverServiceAsync.lookupExternalAccount( + ReceiverLookupExternalAccountParams.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun lookupUma() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val receiverServiceAsync = client.receiver() + + val response = + receiverServiceAsync.lookupUma( + ReceiverLookupUmaParams.builder() + .receiverUmaAddress("receiverUmaAddress") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/SandboxServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/SandboxServiceAsyncTest.kt new file mode 100644 index 00000000..1a2289eb --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/SandboxServiceAsyncTest.kt @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.sandbox.SandboxSendFundsParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class SandboxServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun sendFunds() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val sandboxServiceAsync = client.sandbox() + + val response = + sandboxServiceAsync.sendFunds( + SandboxSendFundsParams.builder() + .currencyCode("USD") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .currencyAmount(1000L) + .build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TokenServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TokenServiceAsyncTest.kt new file mode 100644 index 00000000..e6838602 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TokenServiceAsyncTest.kt @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.tokens.Permission +import com.grid.api.models.tokens.TokenCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TokenServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenServiceAsync = client.tokens() + + val apiToken = + tokenServiceAsync.create( + TokenCreateParams.builder() + .name("Sandbox read-only") + .addPermission(Permission.VIEW) + .build() + ) + + apiToken.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun retrieve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenServiceAsync = client.tokens() + + val apiToken = tokenServiceAsync.retrieve("tokenId") + + apiToken.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenServiceAsync = client.tokens() + + val page = tokenServiceAsync.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun delete() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenServiceAsync = client.tokens() + + tokenServiceAsync.delete("tokenId") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransactionServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransactionServiceAsyncTest.kt new file mode 100644 index 00000000..bc2d27b0 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransactionServiceAsyncTest.kt @@ -0,0 +1,96 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.core.JsonValue +import com.grid.api.models.transactions.TransactionApproveParams +import com.grid.api.models.transactions.TransactionRejectParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TransactionServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun retrieve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionServiceAsync = client.transactions() + + val transaction = transactionServiceAsync.retrieve("transactionId") + + transaction.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionServiceAsync = client.transactions() + + val page = transactionServiceAsync.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun approve() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionServiceAsync = client.transactions() + + val incomingTransaction = + transactionServiceAsync.approve( + TransactionApproveParams.builder() + .transactionId("transactionId") + .receiverCustomerInfo( + TransactionApproveParams.ReceiverCustomerInfo.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + + incomingTransaction.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun reject() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionServiceAsync = client.transactions() + + val incomingTransaction = + transactionServiceAsync.reject( + TransactionRejectParams.builder() + .transactionId("transactionId") + .reason("RESTRICTED_JURISDICTION") + .build() + ) + + incomingTransaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferInServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferInServiceAsyncTest.kt new file mode 100644 index 00000000..beded763 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferInServiceAsyncTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.transferin.TransferInCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TransferInServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transferInServiceAsync = client.transferIn() + + val transaction = + transferInServiceAsync.create( + TransferInCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .amount(12550L) + .build() + ) + + transaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferOutServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferOutServiceAsyncTest.kt new file mode 100644 index 00000000..1e13c06f --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/TransferOutServiceAsyncTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.transferout.TransferOutCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TransferOutServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transferOutServiceAsync = client.transferOut() + + val transaction = + transferOutServiceAsync.create( + TransferOutCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .amount(12550L) + .build() + ) + + transaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncTest.kt new file mode 100644 index 00000000..3dcb9c81 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/UmaProviderServiceAsyncTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class UmaProviderServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val umaProviderServiceAsync = client.umaProviders() + + val page = umaProviderServiceAsync.list() + + page.response().validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/WebhookServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/WebhookServiceAsyncTest.kt new file mode 100644 index 00000000..3561bd66 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/WebhookServiceAsyncTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class WebhookServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun sendTest() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val webhookServiceAsync = client.webhooks() + + val response = webhookServiceAsync.sendTest() + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncTest.kt new file mode 100644 index 00000000..22938ffc --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/BulkServiceAsyncTest.kt @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.customers + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class BulkServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun getJobStatus() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val bulkServiceAsync = client.customers().bulk() + + val response = bulkServiceAsync.getJobStatus("jobId") + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun uploadCsv() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val bulkServiceAsync = client.customers().bulk() + + val response = + bulkServiceAsync.uploadCsv( + BulkUploadCsvParams.builder().file("some content".byteInputStream()).build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncTest.kt new file mode 100644 index 00000000..ecccee1a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/customers/ExternalAccountServiceAsyncTest.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.customers + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ExternalAccountServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountServiceAsync = client.customers().externalAccounts() + + val externalAccount = + externalAccountServiceAsync.create( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + + externalAccount.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountServiceAsync = client.customers().externalAccounts() + + val page = externalAccountServiceAsync.list() + + page.response().validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncTest.kt new file mode 100644 index 00000000..3f86bea5 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/platform/ExternalAccountServiceAsyncTest.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.platform + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import com.grid.api.models.platform.externalaccounts.ExternalAccountListParams +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ExternalAccountServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun create() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountServiceAsync = client.platform().externalAccounts() + + val externalAccount = + externalAccountServiceAsync.create( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + + externalAccount.validate() + } + + @Disabled("Prism tests are disabled") + @Test + suspend fun list() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountServiceAsync = client.platform().externalAccounts() + + val externalAccounts = + externalAccountServiceAsync.list( + ExternalAccountListParams.builder().currency("currency").build() + ) + + externalAccounts.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncTest.kt new file mode 100644 index 00000000..a695d123 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/InternalAccountServiceAsyncTest.kt @@ -0,0 +1,36 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.sandbox + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.sandbox.internalaccounts.InternalAccountFundParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class InternalAccountServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun fund() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val internalAccountServiceAsync = client.sandbox().internalAccounts() + + val internalAccount = + internalAccountServiceAsync.fund( + InternalAccountFundParams.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .amount(100000L) + .build() + ) + + internalAccount.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncTest.kt new file mode 100644 index 00000000..e9363f55 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/async/sandbox/UmaServiceAsyncTest.kt @@ -0,0 +1,39 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.async.sandbox + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClientAsync +import com.grid.api.models.sandbox.uma.UmaReceivePaymentParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class UmaServiceAsyncTest { + + @Disabled("Prism tests are disabled") + @Test + suspend fun receivePayment() { + val client = + GridOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val umaServiceAsync = client.sandbox().uma() + + val incomingTransaction = + umaServiceAsync.receivePayment( + UmaReceivePaymentParams.builder() + .receivingCurrencyAmount(1000L) + .receivingCurrencyCode("USD") + .senderUmaAddress("\$success.usd@sandbox.grid.uma.money") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .receiverUmaAddress("\$receiver@uma.domain") + .build() + ) + + incomingTransaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ConfigServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ConfigServiceTest.kt new file mode 100644 index 00000000..f9455f92 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ConfigServiceTest.kt @@ -0,0 +1,89 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.config.ConfigUpdateParams +import com.grid.api.models.config.CustomerInfoFieldName +import com.grid.api.models.config.PlatformCurrencyConfig +import com.grid.api.models.receiver.CounterpartyFieldDefinition +import com.grid.api.models.transactions.TransactionType +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ConfigServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun retrieve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val configService = client.config() + + val platformConfig = configService.retrieve() + + platformConfig.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun update() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val configService = client.config() + + val platformConfig = + configService.update( + ConfigUpdateParams.builder() + .addSupportedCurrency( + PlatformCurrencyConfig.builder() + .currencyCode("USD") + .addEnabledTransactionType(TransactionType.OUTGOING) + .addEnabledTransactionType(TransactionType.INCOMING) + .maxAmount(1000000L) + .minAmount(100L) + .requiredCounterpartyFields( + listOf( + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.FULL_NAME) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.NATIONALITY) + .build(), + CounterpartyFieldDefinition.builder() + .mandatory(true) + .name(CustomerInfoFieldName.BIRTH_DATE) + .build(), + ) + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.FULL_NAME + ) + .addProviderRequiredCounterpartyCustomerField( + CustomerInfoFieldName.COUNTRY_OF_RESIDENCE + ) + .addProviderRequiredCustomerField(CustomerInfoFieldName.NATIONALITY) + .addProviderRequiredCustomerField(CustomerInfoFieldName.BIRTH_DATE) + .build() + ) + .umaDomain("mycompany.com") + .webhookEndpoint("https://api.mycompany.com/webhooks/uma") + .build() + ) + + platformConfig.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/CustomerServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/CustomerServiceTest.kt new file mode 100644 index 00000000..1ca851d4 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/CustomerServiceTest.kt @@ -0,0 +1,191 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.customers.CustomerCreateParams +import com.grid.api.models.customers.CustomerGetKycLinkParams +import com.grid.api.models.customers.CustomerUpdateParams +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class CustomerServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val customerOneOf = + customerService.create( + CustomerCreateParams.builder() + .createCustomerRequest( + CustomerCreateParams.CreateCustomerRequest.Individual.builder() + .platformCustomerId("9f84e0c2a72c4fa") + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerCreateParams.CreateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerCreateParams.CreateCustomerRequest.Individual.Address + .builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Michael Doe") + .nationality("US") + .build() + ) + .build() + ) + + customerOneOf.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun retrieve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val customer = customerService.retrieve("customerId") + + customer.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun update() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val customer = + customerService.update( + CustomerUpdateParams.builder() + .customerId("customerId") + .updateCustomerRequest( + CustomerUpdateParams.UpdateCustomerRequest.Individual.builder() + .umaAddress("\$john.doe@uma.domain.com") + .customerType( + CustomerUpdateParams.UpdateCustomerRequest.Individual.CustomerType + .INDIVIDUAL + ) + .address( + CustomerUpdateParams.UpdateCustomerRequest.Individual.Address + .builder() + .country("US") + .line1("456 Market St") + .postalCode("94103") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .birthDate(LocalDate.parse("1985-06-15")) + .fullName("John Smith") + .nationality("US") + .build() + ) + .build() + ) + + customer.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val page = customerService.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun delete() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val customer = customerService.delete("customerId") + + customer.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun getKycLink() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val response = + customerService.getKycLink( + CustomerGetKycLinkParams.builder() + .platformCustomerId("platformCustomerId") + .redirectUri("redirectUri") + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun listInternalAccounts() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val customerService = client.customers() + + val page = customerService.listInternalAccounts() + + page.response().validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ExchangeRateServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ExchangeRateServiceTest.kt new file mode 100644 index 00000000..aa416b88 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ExchangeRateServiceTest.kt @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.exchangerates.ExchangeRateListParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ExchangeRateServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val exchangeRateService = client.exchangeRates() + + val exchangeRates = + exchangeRateService.list( + ExchangeRateListParams.builder() + .addDestinationCurrency("string") + .sendingAmount(0L) + .sourceCurrency("sourceCurrency") + .build() + ) + + exchangeRates.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/InvitationServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/InvitationServiceTest.kt new file mode 100644 index 00000000..19f58fce --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/InvitationServiceTest.kt @@ -0,0 +1,94 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.invitations.InvitationClaimParams +import com.grid.api.models.invitations.InvitationCreateParams +import java.time.OffsetDateTime +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class InvitationServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationService = client.invitations() + + val umaInvitation = + invitationService.create( + InvitationCreateParams.builder() + .inviterUma("\$inviter@uma.domain") + .amountToSend(12550L) + .expiresAt(OffsetDateTime.parse("2025-09-01T14:30:00Z")) + .firstName("Alice") + .build() + ) + + umaInvitation.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun retrieve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationService = client.invitations() + + val umaInvitation = invitationService.retrieve("invitationCode") + + umaInvitation.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun cancel() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationService = client.invitations() + + val umaInvitation = invitationService.cancel("invitationCode") + + umaInvitation.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun claim() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val invitationService = client.invitations() + + val umaInvitation = + invitationService.claim( + InvitationClaimParams.builder() + .invitationCode("invitationCode") + .inviteeUma("\$invitee@uma.domain") + .build() + ) + + umaInvitation.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlaidServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlaidServiceTest.kt new file mode 100644 index 00000000..bd3f8934 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlaidServiceTest.kt @@ -0,0 +1,59 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.plaid.PlaidCreateLinkTokenParams +import com.grid.api.models.plaid.PlaidSubmitPublicTokenParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class PlaidServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun createLinkToken() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val plaidService = client.plaid() + + val response = + plaidService.createLinkToken( + PlaidCreateLinkTokenParams.builder() + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun submitPublicToken() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val plaidService = client.plaid() + + val externalAccount = + plaidService.submitPublicToken( + PlaidSubmitPublicTokenParams.builder() + .plaidLinkToken("link-sandbox-abc123xyz-1234-5678") + .publicToken("public-sandbox-12345678-1234-1234-1234-123456789012") + .accountId("plaid_account_id_123") + .build() + ) + + externalAccount.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlatformServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlatformServiceTest.kt new file mode 100644 index 00000000..755e4c24 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/PlatformServiceTest.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.platform.PlatformListInternalAccountsParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class PlatformServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun listInternalAccounts() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val platformService = client.platform() + + val response = + platformService.listInternalAccounts( + PlatformListInternalAccountsParams.builder().currency("currency").build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/QuoteServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/QuoteServiceTest.kt new file mode 100644 index 00000000..754df937 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/QuoteServiceTest.kt @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.core.JsonValue +import com.grid.api.models.quotes.QuoteCreateParams +import com.grid.api.models.quotes.QuoteSourceOneOf +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class QuoteServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteService = client.quotes() + + val quote = + quoteService.create( + QuoteCreateParams.builder() + .accountDestination("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .lockedCurrencyAmount(10000L) + .lockedCurrencySide(QuoteCreateParams.LockedCurrencySide.SENDING) + .source( + QuoteSourceOneOf.Account.builder() + .accountId("InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .sourceType(QuoteSourceOneOf.Account.SourceType.ACCOUNT) + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .build() + ) + .description("Transfer between accounts, either internal or external.") + .immediatelyExecute(false) + .lookupId("Lookup:019542f5-b3e7-1d02-0000-000000000009") + .senderCustomerInfo( + QuoteCreateParams.SenderCustomerInfo.builder() + .putAdditionalProperty("FULL_NAME", JsonValue.from("bar")) + .putAdditionalProperty("NATIONALITY", JsonValue.from("bar")) + .build() + ) + .build() + ) + + quote.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun retrieve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteService = client.quotes() + + val quote = quoteService.retrieve("quoteId") + + quote.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteService = client.quotes() + + val page = quoteService.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun execute() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val quoteService = client.quotes() + + val quote = quoteService.execute("Quote:019542f5-b3e7-1d02-0000-000000000001") + + quote.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ReceiverServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ReceiverServiceTest.kt new file mode 100644 index 00000000..ec783965 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/ReceiverServiceTest.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.receiver.ReceiverLookupExternalAccountParams +import com.grid.api.models.receiver.ReceiverLookupUmaParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ReceiverServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun lookupExternalAccount() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val receiverService = client.receiver() + + val response = + receiverService.lookupExternalAccount( + ReceiverLookupExternalAccountParams.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + ) + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun lookupUma() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val receiverService = client.receiver() + + val response = + receiverService.lookupUma( + ReceiverLookupUmaParams.builder() + .receiverUmaAddress("receiverUmaAddress") + .customerId("customerId") + .senderUmaAddress("senderUmaAddress") + .build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/SandboxServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/SandboxServiceTest.kt new file mode 100644 index 00000000..8588e482 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/SandboxServiceTest.kt @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.sandbox.SandboxSendFundsParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class SandboxServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun sendFunds() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val sandboxService = client.sandbox() + + val response = + sandboxService.sendFunds( + SandboxSendFundsParams.builder() + .currencyCode("USD") + .quoteId("Quote:019542f5-b3e7-1d02-0000-000000000006") + .currencyAmount(1000L) + .build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TokenServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TokenServiceTest.kt new file mode 100644 index 00000000..8c3f7c37 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TokenServiceTest.kt @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.tokens.Permission +import com.grid.api.models.tokens.TokenCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TokenServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenService = client.tokens() + + val apiToken = + tokenService.create( + TokenCreateParams.builder() + .name("Sandbox read-only") + .addPermission(Permission.VIEW) + .build() + ) + + apiToken.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun retrieve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenService = client.tokens() + + val apiToken = tokenService.retrieve("tokenId") + + apiToken.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenService = client.tokens() + + val page = tokenService.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun delete() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val tokenService = client.tokens() + + tokenService.delete("tokenId") + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransactionServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransactionServiceTest.kt new file mode 100644 index 00000000..081184e9 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransactionServiceTest.kt @@ -0,0 +1,96 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.core.JsonValue +import com.grid.api.models.transactions.TransactionApproveParams +import com.grid.api.models.transactions.TransactionRejectParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TransactionServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun retrieve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionService = client.transactions() + + val transaction = transactionService.retrieve("transactionId") + + transaction.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionService = client.transactions() + + val page = transactionService.list() + + page.response().validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun approve() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionService = client.transactions() + + val incomingTransaction = + transactionService.approve( + TransactionApproveParams.builder() + .transactionId("transactionId") + .receiverCustomerInfo( + TransactionApproveParams.ReceiverCustomerInfo.builder() + .putAdditionalProperty("foo", JsonValue.from("bar")) + .build() + ) + .build() + ) + + incomingTransaction.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun reject() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transactionService = client.transactions() + + val incomingTransaction = + transactionService.reject( + TransactionRejectParams.builder() + .transactionId("transactionId") + .reason("RESTRICTED_JURISDICTION") + .build() + ) + + incomingTransaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferInServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferInServiceTest.kt new file mode 100644 index 00000000..e7978a3a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferInServiceTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.transferin.TransferInCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TransferInServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transferInService = client.transferIn() + + val transaction = + transferInService.create( + TransferInCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferInCreateParams.Destination.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .source( + TransferInCreateParams.Source.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .amount(12550L) + .build() + ) + + transaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferOutServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferOutServiceTest.kt new file mode 100644 index 00000000..7f1b7a5a --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/TransferOutServiceTest.kt @@ -0,0 +1,46 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.transferout.TransferOutCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class TransferOutServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val transferOutService = client.transferOut() + + val transaction = + transferOutService.create( + TransferOutCreateParams.builder() + .idempotencyKey("550e8400-e29b-41d4-a716-446655440000") + .destination( + TransferOutCreateParams.Destination.builder() + .accountId("ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965") + .build() + ) + .source( + TransferOutCreateParams.Source.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .build() + ) + .amount(12550L) + .build() + ) + + transaction.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/UmaProviderServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/UmaProviderServiceTest.kt new file mode 100644 index 00000000..057c0705 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/UmaProviderServiceTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class UmaProviderServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val umaProviderService = client.umaProviders() + + val page = umaProviderService.list() + + page.response().validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/WebhookServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/WebhookServiceTest.kt new file mode 100644 index 00000000..8275e0d1 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/WebhookServiceTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class WebhookServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun sendTest() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val webhookService = client.webhooks() + + val response = webhookService.sendTest() + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/BulkServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/BulkServiceTest.kt new file mode 100644 index 00000000..cd6237db --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/BulkServiceTest.kt @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.customers + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.customers.bulk.BulkUploadCsvParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class BulkServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun getJobStatus() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val bulkService = client.customers().bulk() + + val response = bulkService.getJobStatus("jobId") + + response.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun uploadCsv() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val bulkService = client.customers().bulk() + + val response = + bulkService.uploadCsv( + BulkUploadCsvParams.builder().file("some content".byteInputStream()).build() + ) + + response.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceTest.kt new file mode 100644 index 00000000..3782b7f4 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/customers/ExternalAccountServiceTest.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.customers + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ExternalAccountServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountService = client.customers().externalAccounts() + + val externalAccount = + externalAccountService.create( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + + externalAccount.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountService = client.customers().externalAccounts() + + val page = externalAccountService.list() + + page.response().validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceTest.kt new file mode 100644 index 00000000..de896ef7 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/platform/ExternalAccountServiceTest.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.platform + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.customers.externalaccounts.BeneficiaryOneOf +import com.grid.api.models.customers.externalaccounts.ExternalAccountCreate +import com.grid.api.models.customers.externalaccounts.ExternalAccountInfoOneOf +import com.grid.api.models.platform.externalaccounts.ExternalAccountListParams +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class ExternalAccountServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun create() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountService = client.platform().externalAccounts() + + val externalAccount = + externalAccountService.create( + ExternalAccountCreate.builder() + .accountInfo( + ExternalAccountInfoOneOf.UsAccount.builder() + .accountCategory( + ExternalAccountInfoOneOf.UsAccount.AccountCategory.CHECKING + ) + .accountNumber("12345678901") + .beneficiary( + BeneficiaryOneOf.Individual.builder() + .birthDate(LocalDate.parse("1990-01-15")) + .fullName("John Doe") + .nationality("US") + .address( + BeneficiaryOneOf.Individual.Address.builder() + .country("US") + .line1("123 Main Street") + .postalCode("94105") + .city("San Francisco") + .line2("Apt 4B") + .state("CA") + .build() + ) + .build() + ) + .routingNumber("123456789") + .bankName("Chase Bank") + .build() + ) + .currency("USD") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .defaultUmaDepositAccount(true) + .platformAccountId("ext_acc_123456") + .build() + ) + + externalAccount.validate() + } + + @Disabled("Prism tests are disabled") + @Test + fun list() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val externalAccountService = client.platform().externalAccounts() + + val externalAccounts = + externalAccountService.list( + ExternalAccountListParams.builder().currency("currency").build() + ) + + externalAccounts.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceTest.kt new file mode 100644 index 00000000..672517a3 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/InternalAccountServiceTest.kt @@ -0,0 +1,36 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.sandbox + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.sandbox.internalaccounts.InternalAccountFundParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class InternalAccountServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun fund() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val internalAccountService = client.sandbox().internalAccounts() + + val internalAccount = + internalAccountService.fund( + InternalAccountFundParams.builder() + .accountId("InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .amount(100000L) + .build() + ) + + internalAccount.validate() + } +} diff --git a/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceTest.kt b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceTest.kt new file mode 100644 index 00000000..95a2c741 --- /dev/null +++ b/grid-kotlin-core/src/test/kotlin/com/grid/api/services/blocking/sandbox/UmaServiceTest.kt @@ -0,0 +1,39 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.services.blocking.sandbox + +import com.grid.api.TestServerExtension +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.models.sandbox.uma.UmaReceivePaymentParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(TestServerExtension::class) +internal class UmaServiceTest { + + @Disabled("Prism tests are disabled") + @Test + fun receivePayment() { + val client = + GridOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .username("My Username") + .password("My Password") + .build() + val umaService = client.sandbox().uma() + + val incomingTransaction = + umaService.receivePayment( + UmaReceivePaymentParams.builder() + .receivingCurrencyAmount(1000L) + .receivingCurrencyCode("USD") + .senderUmaAddress("\$success.usd@sandbox.grid.uma.money") + .customerId("Customer:019542f5-b3e7-1d02-0000-000000000001") + .receiverUmaAddress("\$receiver@uma.domain") + .build() + ) + + incomingTransaction.validate() + } +} diff --git a/grid-kotlin-example/build.gradle.kts b/grid-kotlin-example/build.gradle.kts new file mode 100644 index 00000000..e7af5279 --- /dev/null +++ b/grid-kotlin-example/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("grid.kotlin") + application +} + +dependencies { + implementation(project(":grid-kotlin-core")) + implementation(project(":grid-kotlin-client-okhttp")) +} + +application { + // Use `./gradlew :grid-kotlin-example:run` to run `Main` + // Use `./gradlew :grid-kotlin-example:run -Pexample=Something` to run `SomethingExample` + mainClass = "com.grid.api.example.${ + if (project.hasProperty("example")) + "${project.property("example")}ExampleKt" + else + "MainKt" + }" +} diff --git a/grid-kotlin-lib/.keep b/grid-kotlin-lib/.keep new file mode 100644 index 00000000..5e2c99fd --- /dev/null +++ b/grid-kotlin-lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/grid-kotlin-proguard-test/build.gradle.kts b/grid-kotlin-proguard-test/build.gradle.kts new file mode 100644 index 00000000..2b81b406 --- /dev/null +++ b/grid-kotlin-proguard-test/build.gradle.kts @@ -0,0 +1,101 @@ +plugins { + id("grid.kotlin") + id("com.gradleup.shadow") version "8.3.8" +} + +buildscript { + repositories { + google() + } + + dependencies { + classpath("com.guardsquare:proguard-gradle:7.4.2") + classpath("com.android.tools:r8:8.3.37") + } +} + +dependencies { + testImplementation(project(":grid-kotlin")) + testImplementation(kotlin("test")) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") + testImplementation("org.assertj:assertj-core:3.27.7") + testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0") +} + +tasks.shadowJar { + from(sourceSets.test.get().output) + configurations = listOf(project.configurations.testRuntimeClasspath.get()) +} + +val proguardJarPath = "${layout.buildDirectory.get()}/libs/${project.name}-${project.version}-proguard.jar" +val proguardJar by tasks.registering(proguard.gradle.ProGuardTask::class) { + group = "verification" + dependsOn(tasks.shadowJar) + notCompatibleWithConfigurationCache("ProGuard") + + injars(tasks.shadowJar) + outjars(proguardJarPath) + printmapping("${layout.buildDirectory.get()}/proguard-mapping.txt") + + val javaHome = System.getProperty("java.home") + if (System.getProperty("java.version").startsWith("1.")) { + // Before Java 9, the runtime classes were packaged in a single jar file. + libraryjars("$javaHome/lib/rt.jar") + } else { + // As of Java 9, the runtime classes are packaged in modular jmod files. + libraryjars( + // Filters must be specified first, as a map. + mapOf("jarfilter" to "!**.jar", "filter" to "!module-info.class"), + "$javaHome/jmods/java.base.jmod" + ) + } + + configuration("./test.pro") + configuration("../grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro") +} + +val testProGuard by tasks.registering(JavaExec::class) { + group = "verification" + dependsOn(proguardJar) + notCompatibleWithConfigurationCache("ProGuard") + + mainClass.set("com.grid.api.proguard.ProGuardCompatibilityTest") + classpath = files(proguardJarPath) +} + +val r8JarPath = "${layout.buildDirectory.get()}/libs/${project.name}-${project.version}-r8.jar" +val r8Jar by tasks.registering(JavaExec::class) { + group = "verification" + dependsOn(tasks.shadowJar) + notCompatibleWithConfigurationCache("R8") + + mainClass.set("com.android.tools.r8.R8") + classpath = buildscript.configurations["classpath"] + + args = listOf( + "--release", + "--classfile", + "--output", r8JarPath, + "--lib", System.getProperty("java.home"), + "--pg-conf", "./test.pro", + "--pg-conf", "../grid-kotlin-core/src/main/resources/META-INF/proguard/grid-kotlin-core.pro", + "--pg-map-output", "${layout.buildDirectory.get()}/r8-mapping.txt", + tasks.shadowJar.get().archiveFile.get().asFile.absolutePath, + ) +} + +val testR8 by tasks.registering(JavaExec::class) { + group = "verification" + dependsOn(r8Jar) + notCompatibleWithConfigurationCache("R8") + + mainClass.set("com.grid.api.proguard.ProGuardCompatibilityTest") + classpath = files(r8JarPath) +} + +tasks.test { + dependsOn(testProGuard) + dependsOn(testR8) + // We defer to the tests run via the ProGuard JAR. + enabled = false +} diff --git a/grid-kotlin-proguard-test/src/test/kotlin/com/grid/api/proguard/ProGuardCompatibilityTest.kt b/grid-kotlin-proguard-test/src/test/kotlin/com/grid/api/proguard/ProGuardCompatibilityTest.kt new file mode 100644 index 00000000..f1edaf55 --- /dev/null +++ b/grid-kotlin-proguard-test/src/test/kotlin/com/grid/api/proguard/ProGuardCompatibilityTest.kt @@ -0,0 +1,123 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.grid.api.proguard + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.grid.api.client.okhttp.GridOkHttpClient +import com.grid.api.core.jsonMapper +import com.grid.api.models.config.CustomerInfoFieldName +import com.grid.api.models.quotes.Currency +import com.grid.api.models.quotes.QuoteDestinationOneOf +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.javaMethod +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ProGuardCompatibilityTest { + + companion object { + + @JvmStatic + fun main(args: Array) { + // To debug that we're using the right JAR. + val jarPath = this::class.java.getProtectionDomain().codeSource.location + println("JAR being used: $jarPath") + + // We have to manually run the test methods instead of using the JUnit runner because it + // seems impossible to get working with R8. + val test = ProGuardCompatibilityTest() + test::class + .memberFunctions + .asSequence() + .filter { function -> + function.javaMethod?.isAnnotationPresent(Test::class.java) == true + } + .forEach { it.call(test) } + } + } + + @Test + fun proguardRules() { + val rulesFile = + javaClass.classLoader.getResourceAsStream("META-INF/proguard/grid-kotlin-core.pro") + + assertThat(rulesFile).isNotNull() + } + + @Test + fun client() { + val client = + GridOkHttpClient.builder().username("My Username").password("My Password").build() + + assertThat(client).isNotNull() + assertThat(client.config()).isNotNull() + assertThat(client.customers()).isNotNull() + assertThat(client.platform()).isNotNull() + assertThat(client.plaid()).isNotNull() + assertThat(client.transferIn()).isNotNull() + assertThat(client.transferOut()).isNotNull() + assertThat(client.receiver()).isNotNull() + assertThat(client.quotes()).isNotNull() + assertThat(client.transactions()).isNotNull() + assertThat(client.webhooks()).isNotNull() + assertThat(client.invitations()).isNotNull() + assertThat(client.sandbox()).isNotNull() + assertThat(client.umaProviders()).isNotNull() + assertThat(client.tokens()).isNotNull() + assertThat(client.exchangeRates()).isNotNull() + } + + @Test + fun currencyRoundtrip() { + val jsonMapper = jsonMapper() + val currency = + Currency.builder() + .code("USD") + .decimals(2L) + .name("United States Dollar") + .symbol("\$") + .build() + + val roundtrippedCurrency = + jsonMapper.readValue( + jsonMapper.writeValueAsString(currency), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCurrency).isEqualTo(currency) + } + + @Test + fun quoteDestinationOneOfRoundtrip() { + val jsonMapper = jsonMapper() + val quoteDestinationOneOf = + QuoteDestinationOneOf.ofAccount( + QuoteDestinationOneOf.Account.builder() + .accountId("ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123") + .destinationType(QuoteDestinationOneOf.Account.DestinationType.ACCOUNT) + .build() + ) + + val roundtrippedQuoteDestinationOneOf = + jsonMapper.readValue( + jsonMapper.writeValueAsString(quoteDestinationOneOf), + jacksonTypeRef(), + ) + + assertThat(roundtrippedQuoteDestinationOneOf).isEqualTo(quoteDestinationOneOf) + } + + @Test + fun customerInfoFieldNameRoundtrip() { + val jsonMapper = jsonMapper() + val customerInfoFieldName = CustomerInfoFieldName.FULL_NAME + + val roundtrippedCustomerInfoFieldName = + jsonMapper.readValue( + jsonMapper.writeValueAsString(customerInfoFieldName), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCustomerInfoFieldName).isEqualTo(customerInfoFieldName) + } +} diff --git a/grid-kotlin-proguard-test/test.pro b/grid-kotlin-proguard-test/test.pro new file mode 100644 index 00000000..83f053dd --- /dev/null +++ b/grid-kotlin-proguard-test/test.pro @@ -0,0 +1,9 @@ +# Specify the entrypoint where ProGuard starts to determine what's reachable. +-keep class com.grid.api.proguard.** { *; } + +# For the testing framework. +-keep class org.junit.** { *; } + +# Many warnings don't apply for our testing purposes. +-dontnote +-dontwarn \ No newline at end of file diff --git a/grid-kotlin/build.gradle.kts b/grid-kotlin/build.gradle.kts new file mode 100644 index 00000000..cd5c5052 --- /dev/null +++ b/grid-kotlin/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("grid.kotlin") + id("grid.publish") +} + +dependencies { + api(project(":grid-kotlin-client-okhttp")) +} + +// Redefine `dokkaHtml` to: +// - Depend on the root project's task for merging the docs of all the projects +// - Forward that task's output to this task's output +tasks.named("dokkaHtml").configure { + actions.clear() + + val dokkaHtmlCollector = rootProject.tasks["dokkaHtmlCollector"] + dependsOn(dokkaHtmlCollector) + + val outputDirectory = project.layout.buildDirectory.dir("dokka/html") + doLast { + copy { + from(dokkaHtmlCollector.outputs.files) + into(outputDirectory) + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } + + outputs.dir(outputDirectory) +} diff --git a/mintlify/AGENTS.md b/mintlify/AGENTS.md deleted file mode 100644 index b7c326eb..00000000 --- a/mintlify/AGENTS.md +++ /dev/null @@ -1,396 +0,0 @@ -# Mintlify technical writing rule - -You are an AI writing assistant specialized in creating exceptional technical documentation using Mintlify components and following industry-leading technical writing practices. Your docs are clear, succinct and well structured. They are the right balance of enough depth to understand but not too much to confuse. Your goal is to create user friendly content that enables developers, customers, and internal teams to understand and effectively use products, APIs and systems. - - -## Core writing principles - -### Language and style requirements - -- Use clear, direct language appropriate for technical audiences -- Write in second person ("you") for instructions and procedures -- Use active voice over passive voice -- Employ present tense for current states, future tense for outcomes -- Avoid jargon unless necessary and define terms when first used -- Maintain consistent terminology throughout all documentation -- Keep sentences concise while providing necessary context -- Use parallel structure in lists, headings, and procedures - -### Content organization standards - -- Lead with the most important information (inverted pyramid structure) -- Use progressive disclosure: basic concepts before advanced ones -- Break complex procedures into numbered steps -- Include prerequisites and context before instructions -- Provide expected outcomes for each major step -- Use descriptive, keyword-rich headings for navigation and SEO -- Group related information logically with clear section breaks - -### User-centered approach - -- Focus on user goals and outcomes rather than system features -- Anticipate common questions and address them proactively -- Include troubleshooting for likely failure points -- Write for scannability with clear headings, lists, and white space -- Include verification steps to confirm success - -## Mintlify component reference - -### Callout components - -#### Note - Additional helpful information - - -Supplementary information that supports the main content without interrupting flow - - -#### Tip - Best practices and pro tips - - -Expert advice, shortcuts, or best practices that enhance user success - - -#### Warning - Important cautions - - -Critical information about potential issues, breaking changes, or destructive actions - - -#### Info - Neutral contextual information - - -Background information, context, or neutral announcements - - -#### Check - Success confirmations - - -Positive confirmations, successful completions, or achievement indicators - - -### Code components - -#### Single code block - -Example of a single code block: - -```javascript config.js -const apiConfig = { - baseURL: 'https://api.example.com', - timeout: 5000, - headers: { - 'Authorization': `Bearer ${process.env.API_TOKEN}` - } -}; -``` - -#### Code group with multiple languages - -Example of a code group: - - -```javascript Node.js -const response = await fetch('/api/endpoint', { - headers: { Authorization: `Bearer ${apiKey}` } -}); -``` - -```python Python -import requests -response = requests.get('/api/endpoint', - headers={'Authorization': f'Bearer {api_key}'}) -``` - -```curl cURL -curl -X GET '/api/endpoint' \ - -H 'Authorization: Bearer YOUR_API_KEY' -``` - - -#### Request/response examples - -Example of request/response documentation: - - -```bash cURL -curl -X POST 'https://api.example.com/users' \ - -H 'Content-Type: application/json' \ - -d '{"name": "John Doe", "email": "john@example.com"}' -``` - - - -```json Success -{ - "id": "user_123", - "name": "John Doe", - "email": "john@example.com", - "created_at": "2024-01-15T10:30:00Z" -} -``` - - -### Structural components - -#### Steps for procedures - -Example of step-by-step instructions: - - - - Run `npm install` to install required packages. - - - Verify installation by running `npm list`. - - - - - Create a `.env` file with your API credentials. - - ```bash - API_KEY=your_api_key_here - ``` - - - Never commit API keys to version control. - - - - -#### Tabs for alternative content - -Example of tabbed content: - - - - ```bash - brew install node - npm install -g package-name - ``` - - - - ```powershell - choco install nodejs - npm install -g package-name - ``` - - - - ```bash - sudo apt install nodejs npm - npm install -g package-name - ``` - - - -#### Accordions for collapsible content - -Example of accordion groups: - - - - - **Firewall blocking**: Ensure ports 80 and 443 are open - - **Proxy configuration**: Set HTTP_PROXY environment variable - - **DNS resolution**: Try using 8.8.8.8 as DNS server - - - - ```javascript - const config = { - performance: { cache: true, timeout: 30000 }, - security: { encryption: 'AES-256' } - }; - ``` - - - -### Cards and columns for emphasizing information - -Example of cards and card groups: - - -Complete walkthrough from installation to your first API call in under 10 minutes. - - - - - Learn how to authenticate requests using API keys or JWT tokens. - - - - Understand rate limits and best practices for high-volume usage. - - - -### API documentation components - -#### Parameter fields - -Example of parameter documentation: - - -Unique identifier for the user. Must be a valid UUID v4 format. - - - -User's email address. Must be valid and unique within the system. - - - -Maximum number of results to return. Range: 1-100. - - - -Bearer token for API authentication. Format: `Bearer YOUR_API_KEY` - - -#### Response fields - -Example of response field documentation: - - -Unique identifier assigned to the newly created user. - - - -ISO 8601 formatted timestamp of when the user was created. - - - -List of permission strings assigned to this user. - - -#### Expandable nested fields - -Example of nested field documentation: - - -Complete user object with all associated data. - - - - User profile information including personal details. - - - - User's first name as entered during registration. - - - - URL to user's profile picture. Returns null if no avatar is set. - - - - - - -### Media and advanced components - -#### Frames for images - -Wrap all images in frames: - - -Main dashboard showing analytics overview - - - -Analytics dashboard with charts - - -#### Videos - -Use the HTML video element for self-hosted video content: - - - -Embed YouTube videos using iframe elements: - - - -#### Tooltips - -Example of tooltip usage: - - -API - - -#### Updates - -Use updates for changelogs: - - -## New features -- Added bulk user import functionality -- Improved error messages with actionable suggestions - -## Bug fixes -- Fixed pagination issue with large datasets -- Resolved authentication timeout problems - - -## Required page structure - -Every documentation page must begin with YAML frontmatter: - -```yaml ---- -title: "Clear, specific, keyword-rich title" -description: "Concise description explaining page purpose and value" ---- -``` - -## Content quality standards - -### Code examples requirements - -- Always include complete, runnable examples that users can copy and execute -- Show proper error handling and edge case management -- Use realistic data instead of placeholder values -- Include expected outputs and results for verification -- Test all code examples thoroughly before publishing -- Specify language and include filename when relevant -- Add explanatory comments for complex logic -- Never include real API keys or secrets in code examples - -### API documentation requirements - -- Document all parameters including optional ones with clear descriptions -- Show both success and error response examples with realistic data -- Include rate limiting information with specific limits -- Provide authentication examples showing proper format -- Explain all HTTP status codes and error handling -- Cover complete request/response cycles - -### Accessibility requirements - -- Include descriptive alt text for all images and diagrams -- Use specific, actionable link text instead of "click here" -- Ensure proper heading hierarchy starting with H2 -- Provide keyboard navigation considerations -- Use sufficient color contrast in examples and visuals -- Structure content for easy scanning with headers and lists - -## Component selection logic - -- Use **Steps** for procedures and sequential instructions -- Use **Tabs** for platform-specific content or alternative approaches -- Use **CodeGroup** when showing the same concept in multiple programming languages -- Use **Accordions** for progressive disclosure of information -- Use **RequestExample/ResponseExample** specifically for API endpoint documentation -- Use **ParamField** for API parameters, **ResponseField** for API responses -- Use **Expandable** for nested object properties or hierarchical information \ No newline at end of file diff --git a/mintlify/CLAUDE.md b/mintlify/CLAUDE.md deleted file mode 100644 index 4749ee6a..00000000 --- a/mintlify/CLAUDE.md +++ /dev/null @@ -1,46 +0,0 @@ -# Mintlify documentation - -## Working relationship -- You can push back on ideas-this can lead to better documentation. Cite sources and explain your reasoning when you do so -- ALWAYS ask for clarification rather than making assumptions -- NEVER lie, guess, or make up information - -## Project context -- Format: MDX files with YAML frontmatter -- Config: docs.json for navigation, theme, settings -- Components: Mintlify components - -## Content strategy -- Document just enough for user success - not too much, not too little -- Prioritize accuracy and usability of information -- Make content evergreen when possible -- Search for existing information before adding new content. Avoid duplication unless it is done for a strategic reason -- Check existing patterns for consistency -- Start by making the smallest reasonable changes - -## Frontmatter requirements for pages -- title: Clear, descriptive page title -- description: Concise summary for SEO/navigation - -## Writing standards -- Second-person voice ("you") -- Prerequisites at start of procedural content -- Test all code examples before publishing -- Match style and formatting of existing pages -- Include both basic and advanced use cases -- Language tags on all code blocks -- Alt text on all images -- Relative paths for internal links - -## Git workflow -- NEVER use --no-verify when committing -- Ask how to handle uncommitted changes before starting -- Create a new branch when no clear branch exists for changes -- Commit frequently throughout development -- NEVER skip or disable pre-commit hooks - -## Do not -- Skip frontmatter on any MDX file -- Use absolute URLs for internal links -- Include untested code examples -- Make assumptions - always ask for clarification \ No newline at end of file diff --git a/mintlify/README.md b/mintlify/README.md deleted file mode 100644 index 9b9c1ddc..00000000 --- a/mintlify/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Grid Documentation - -Grid documentation is built on Mintlify. You can find Mintlify developer docs [here](https://www.mintlify.com/docs). - -## Previewing Documentation - -Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview your documentation changes locally. To install, use the following command: - -``` -npm i -g mint -``` - -Run the following command at the root of the documentation (where docs.json is). - -``` -mint dev -``` - -View your local preview at `http://localhost:3000`. - -## Publishing changes - -Changes are automatically picked up and deployed. diff --git a/mintlify/api-reference/authentication.mdx b/mintlify/api-reference/authentication.mdx deleted file mode 100644 index 5d016130..00000000 --- a/mintlify/api-reference/authentication.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "Authentication" ---- -import { topLevelProductName } from '/snippets/variables.mdx' - -{topLevelProductName} API uses HTTP Basic Authentication with your client ID as the username and your client secret as the password. -Your API tokens are scoped to either your production or sandbox environment. - -```bash -curl -u "{client_id}:{client_secret}" https://api.lightspark.com/grid/2025-10-13... -``` - -If integrating with one of our SDKs, the SDK reads environment variables and will populate the credentials for you. diff --git a/mintlify/api-reference/environments.mdx b/mintlify/api-reference/environments.mdx deleted file mode 100644 index c522eaf6..00000000 --- a/mintlify/api-reference/environments.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: 'Environments' ---- -import { topLevelProductName } from '/snippets/variables.mdx' - -{topLevelProductName} provides two environments: production and sandbox. Both environments use the same base URL and API tokens are scoped to an environment. - -## Base API URL - `https://api.lightspark.com/grid/2025-10-13` - -## Sandbox -Sandbox enables you to test your integration without making real payments. In sandbox, we expose sandbox specific APIs to trigger specific test cases like incoming payments. Additionally you'll find test UMA addresses to simulate different sending scenarios. For more information, see [Sandbox Testing](../payouts-and-b2b/platform-tools/sandbox-testing). - -## Production -Production moves real money. To get access to a production environment, please reach out to your Lightspark contact. - -## Service IPs - -Grid APIs and webhooks are served from the following IP addresses: - -- `52.42.15.30` -- `34.216.87.164` -- `44.226.21.146` - -These IPs are subject to change, but we will notify the account contact email before making any changes. \ No newline at end of file diff --git a/mintlify/api-reference/terminology.mdx b/mintlify/api-reference/terminology.mdx deleted file mode 100644 index d90a2a1d..00000000 --- a/mintlify/api-reference/terminology.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Core Concepts" -description: "Core concepts and terminology for the Grid API" ---- - -import Terminology from '/snippets/terminology.mdx'; - - \ No newline at end of file diff --git a/mintlify/developer-resources/samples.mdx b/mintlify/developer-resources/samples.mdx deleted file mode 100644 index 2ee813ad..00000000 --- a/mintlify/developer-resources/samples.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "Samples" ---- - - -Explore end-to-end sample apps and snippets in our public repository. - - - -Browse runnable examples for quickstarts and integrations. - - - diff --git a/mintlify/docs.json b/mintlify/docs.json deleted file mode 100644 index 78476a62..00000000 --- a/mintlify/docs.json +++ /dev/null @@ -1,312 +0,0 @@ -{ - "$schema": "https://mintlify.com/docs.json", - "theme": "mint", - "name": "Grid API Documentation", - "description": "The Grid API enables modern financial institutions to easily send and receive global payments. The following documentation helps developers understand the Grid API and how to integrate with it.", - "colors": { - "primary": "#1A1A1A", - "light": "#f0f0ee", - "dark": "#00B3E0" - }, - "background": { - "color": { - "light": "#F8F8F7", - "dark": "#000000" - } - }, - "fonts": { - "heading": { - "family": "Suisse Intl", - "source": "/fonts/suisse-intl/SuisseIntl-Medium.woff2", - "format": "woff2", - "weight": 500 - }, - "body": { - "family": "Suisse Intl", - "source": "/fonts/suisse-intl/SuisseIntl-Regular.woff2", - "format": "woff2", - "weight": 400 - } - }, - "favicon": "/favicon.svg", - "navigation": { - "tabs": [ - { - "tab": "Home", - "pages": ["index"] - }, - { - "tab": "Platform Overview", - "pages": [ - { - "group": "Introduction", - "pages": [ - "platform-overview/introduction/what-is-grid", - "platform-overview/introduction/faq" - ] - }, - { - "group": "Core Concepts", - "pages": [ - "platform-overview/core-concepts/entities", - "platform-overview/core-concepts/quote-system", - "platform-overview/core-concepts/account-model", - "platform-overview/core-concepts/transaction-lifecycle", - "platform-overview/core-concepts/currencies-and-rails" - ] - } - ] - }, - { - "tab": "Payouts & B2B", - "pages": [ - "payouts-and-b2b/index", - "payouts-and-b2b/terminology", - "payouts-and-b2b/onboarding/implementation-overview", - "payouts-and-b2b/quickstart", - { - "group": "Onboarding", - "pages": [ - "payouts-and-b2b/onboarding/platform-configuration", - "payouts-and-b2b/onboarding/configuring-customers" - ] - }, - { - "group": "Managing Accounts", - "pages": [ - "payouts-and-b2b/depositing-funds/internal-accounts", - "payouts-and-b2b/depositing-funds/external-accounts", - "payouts-and-b2b/depositing-funds/plaid" - ] - }, - { - "group": "Sending Payments", - "pages": [ - "payouts-and-b2b/depositing-funds/depositing-funds", - "payouts-and-b2b/payment-flow/send-payment", - "payouts-and-b2b/payment-flow/list-transactions", - "payouts-and-b2b/payment-flow/reconciliation", - "payouts-and-b2b/payment-flow/error-handling" - ] - }, - { - "group": "Platform Tools", - "pages": [ - "payouts-and-b2b/platform-tools/webhooks", - "payouts-and-b2b/platform-tools/sandbox-testing", - "payouts-and-b2b/platform-tools/postman-collection" - ] - } - ] - }, - { - "tab": "Ramps", - "pages": [ - "ramps/index", - "ramps/terminology", - "ramps/onboarding/implementation-overview", - "ramps/quickstart", - { - "group": "Onboarding", - "pages": [ - "ramps/onboarding/platform-configuration", - "ramps/onboarding/configuring-customers" - ] - }, - { - "group": "Managing Accounts", - "pages": [ - "ramps/accounts/internal-accounts", - "ramps/accounts/external-accounts", - "ramps/accounts/plaid" - ] - }, - { - "group": "Conversion Flows", - "pages": [ - "ramps/accounts/depositing-funds", - "ramps/conversion-flows/fiat-crypto-conversion", - "ramps/conversion-flows/self-custody-wallets" - ] - }, - { - "group": "Platform Tools", - "pages": [ - "ramps/platform-tools/webhooks", - "ramps/platform-tools/sandbox-testing", - "ramps/platform-tools/postman-collection" - ] - } - ] - }, - { - "tab": "Rewards", - "pages": [ - "rewards/index", - "rewards/terminology", - "rewards/developer-guides/implementation-overview", - "rewards/quickstart", - { - "group": "Onboarding", - "pages": [ - "rewards/developer-guides/platform-configuration" - ] - }, - { - "group": "Managing Accounts", - "pages": [ - "rewards/developer-guides/internal-accounts", - "rewards/developer-guides/external-accounts", - "rewards/developer-guides/plaid" - ] - }, - { - "group": "Distributing Rewards", - "pages": [ - "rewards/developer-guides/distributing-rewards", - "rewards/developer-guides/listing-transactions" - ] - }, - { - "group": "Platform Tools", - "pages": [ - "rewards/platform-tools/webhooks", - "rewards/platform-tools/sandbox-testing", - "rewards/platform-tools/postman-collection" - ] - } - ] - }, - { - "tab": "Global P2P", - "pages": [ - "global-p2p/index", - "global-p2p/terminology", - "global-p2p/getting-started/implementation-overview", - "global-p2p/quickstart", - { - "group": "Onboarding", - "pages": [ - "global-p2p/getting-started/platform-configuration", - "global-p2p/onboarding-customers/configuring-customers", - "global-p2p/onboarding-customers/invitations" - ] - }, - { - "group": "Managing Accounts", - "pages": [ - "global-p2p/managing-accounts/internal-accounts", - "global-p2p/managing-accounts/external-accounts", - "global-p2p/managing-accounts/plaid" - ] - }, - { - "group": "Sending & Receiving Payments", - "pages": [ - "global-p2p/sending-receiving-payments/depositing-funds", - "global-p2p/sending-receiving-payments/sending-payments", - "global-p2p/sending-receiving-payments/receiving-payments", - "global-p2p/sending-receiving-payments/reconciliation", - "global-p2p/sending-receiving-payments/error-handling" - ] - }, - { - "group": "Platform Tools", - "pages": [ - "global-p2p/platform-tools/webhooks", - "global-p2p/platform-tools/sandbox-testing", - "global-p2p/platform-tools/postman-collection", - "global-p2p/platform-tools/uma-test-wallet" - ] - } - ] - }, - { - "tab": "API Reference", - "groups": [ - { - "group": "Using the API", - "pages": [ - "api-reference/environments", - "api-reference/terminology", - "api-reference/authentication" - ] - }, - { - "group": "API documentation", - "openapi": "openapi.yaml" - } - ] - } - ] - }, - "logo": { - "light": "/logo/light.svg", - "dark": "/logo/dark.svg" - }, - "navbar": { - "links": [ - { - "label": "Github", - "href": "https://github.com/lightsparkdev/grid-api", - "icon": "github" - } - ], - "primary": { - "type": "button", - "label": "Contact Sales", - "href": "https://lightspark.com/grid#signup" - } - }, - "contextual": { - "options": ["copy", "view", "chatgpt", "claude"] - }, - "footer": { - "socials": { - "x": "https://x.com/lightspark", - "github": "https://github.com/lightsparkdev", - "linkedin": "https://linkedin.com/company/lightspark-group" - } - }, - "head": { - "links": [ - { - "rel": "preload", - "href": "/fonts/suisse-intl/SuisseIntl-Regular.woff2", - "as": "font", - "type": "font/woff2", - "crossOrigin": "anonymous" - }, - { - "rel": "preload", - "href": "/fonts/suisse-intl/SuisseIntl-Book.woff2", - "as": "font", - "type": "font/woff2", - "crossOrigin": "anonymous" - }, - { - "rel": "preload", - "href": "/fonts/suisse-intl/SuisseIntl-Medium.woff2", - "as": "font", - "type": "font/woff2", - "crossOrigin": "anonymous" - }, - { - "rel": "preload", - "href": "/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2", - "as": "font", - "type": "font/woff2", - "crossOrigin": "anonymous" - }, - { - "rel": "preload", - "href": "/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2", - "as": "font", - "type": "font/woff2", - "crossOrigin": "anonymous" - }, - { "rel": "stylesheet", "href": "/styles/fonts.css" }, - { "rel": "stylesheet", "href": "/styles/styles.css" } - ] - } -} diff --git a/mintlify/favicon.svg b/mintlify/favicon.svg deleted file mode 100644 index 70b3d0e6..00000000 --- a/mintlify/favicon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/mintlify/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2 b/mintlify/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2 deleted file mode 100644 index 945c48ccc1a4995417c773f3b746e04a85c9f37b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17300 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 diff --git a/mintlify/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2 b/mintlify/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2 deleted file mode 100644 index 894e802874713916136ee28874493f60c60687fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17284 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 iyvu1>Yy5*p4RuaF5v5*??Mi19O7f;0z \ No newline at end of file diff --git a/mintlify/global-p2p/getting-started/implementation-overview.mdx b/mintlify/global-p2p/getting-started/implementation-overview.mdx deleted file mode 100644 index 67b507b5..00000000 --- a/mintlify/global-p2p/getting-started/implementation-overview.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: "Implementation Overview" -description: "High-level implementation plan for configuring, onboarding, funding, and moving money" ---- -This page gives you a 10,000‑ft view of an end‑to‑end implementation. It is intentionally generalized because the flow supports multiple customer types and external account types (e.g., CLABE, IBAN, US accounts, UPI). The detailed guides that follow provide concrete fields, edge cases, and step‑by‑step instructions. - -## Platform configuration -Configure your platform once before building user flows. -- Set your UMA domain and brand details used for UMA addressing -- Configure required counterparty and compliance fields for your target corridors -- Provide webhook endpoints for outgoing and incoming payment notifications -- Generate API credentials for Sandbox (and later Production) -- Review regional capabilities (rails, currencies, settlement windows) - -## Customer creation and onboarding -Onboard customers and assign UMA addresses if appropriate. There are two patterns: - -- Regulated entities can directly create customers by providing KYC/KYB data via API -- Unregulated entities should request a KYC link and embed the hosted KYC flow; once completed, the customer can transact -- Store platform customer IDs and UMA handles for use in payment flows - -## Account funding -Choose how transactions are funded based on your product design and region. - -- Prefunded: Maintain balances in one or more currencies and spend from those balances -- Just‑in‑time (JIT): Create a quote and fund it in real time using the payment instructions provided; ideal when you don’t wish to hold float - - -You can mix models: keep small operational float for common corridors and use JIT for long tail routes. - - -## External account creation (payout destinations) -Register payout accounts your customers will send to (or receive from), such as CLABE (MX), IBAN (EU/UK), US accounts, UPI (IN), and others. -- Capture beneficiary details (individual or business) and required banking fields -- Validate account formats where applicable and map them to your internal customer - -## Sending payments -Sending consists of lookup, pricing, funding, and execution. -- Resolve the counterparty: look up receiver information (UMA or bank account) for compliance review and to determine capabilities -- Create a quote: specify source/destination, currencies, and whether you lock sending or receiving amount; receive exchange rate, limits, fees, and (for JIT) funding instructions -- Fund and execute: for prefunded, confirm/execute; for JIT, push funds exactly as instructed (amount, reference) and the platform handles FX and delivery -- Observe status via webhooks and surface outcomes in your UI - - -When sending UMA payments, the sender can retrieve counterparty information before initiating to support compliance and risk checks. - - -## Receiving payments -Enable customers to receive funds to their UMA or linked bank account. -- Expose customer UMA/addressing to payers -- The platform handles conversion and offramping to the receiver’s account currency -- Approve or auto‑approve per your policy; update balances on completion via webhooks - -## Reconciling transactions -Implement operational processes to keep your ledger in sync. -- Process webhooks idempotently; map statuses (pending, processing, completed, failed) -- Tie transactions back to quotes and customers; persist references -- Produce statements and audit trails; handle refunds, retries, and dispute flows where applicable - -## Testing in Sandbox -Use Sandbox to build and validate end‑to‑end without moving real funds. -- Exercise receiver lookup, quote creation, funding instructions, and webhook lifecycles -- Validate compliance decisioning with realistic but synthetic data -- Optionally use the Test Wallet as a counterparty for faster iteration (see Tools) - -## Enabling Production -When you’re ready to go live: -- Complete corridor and provider onboarding as needed for your regions -- Confirm webhook security, monitoring, and alerting are in place -- Review rate limits, error handling, retries, and idempotency keys -- Run final UAT in Sandbox, then request Production access from our team - - -Contact our team to enable Production and finalize corridor activations. - - -## Support -If you need assistance with the Grid API, please contact our support team -at [support@lightspark.com](mailto:support@lightspark.com) or visit our support -portal at [https://support.lightspark.com](https://support.lightspark.com). - - diff --git a/mintlify/global-p2p/getting-started/platform-configuration.mdx b/mintlify/global-p2p/getting-started/platform-configuration.mdx deleted file mode 100644 index b0e7059f..00000000 --- a/mintlify/global-p2p/getting-started/platform-configuration.mdx +++ /dev/null @@ -1,203 +0,0 @@ ---- -title: "Platform Configuration" -description: "Configuring credentials, webhooks and currencies for your platform global P2P payments" ---- -import PlatformConfigAPI from '/snippets/platform-config-currency-api-webhooks.mdx'; -import { topLevelProductName } from '/snippets/variables.mdx'; - - - - -## UMA configuration (optional) -To send and receive using UMA with your own domain (e.g., `$alice@yourdomain.com`), configure the following: - -1. Configure your UMA domain -2. Proxy inbound UMA requests to {topLevelProductName} -3. Define supported currencies and, if you are a regulated institution, the counterparty information you require - -If you do not configure an UMA domain, Grid will use the default domain `grid.lightspark.com`. - -You can find more information about the UMA protocol and end user experience at [https://uma.me](https://uma.me). - -### UMA domain - -The `umaDomain` parameter defines the domain part of all UMA addresses for your users. For example, if you set `umaDomain` to `mycompany.com`, your users' UMA addresses will follow the format `$username@mycompany.com`. - -### Configure UMA proxy requests -Set up proxying so UMA‑related requests are forwarded to your assigned `proxyUmaSubdomain`. - -- UMA domain determines the address format (e.g., `$alice@yourdomain.com`) -- Proxy the following paths to `{proxyUmaSubdomain}`: - - `https:///.well-known/lnurlp/*` -> `https://.grid.lightspark.com/.well-known/lnurlp/*` - - `https:///.well-known/lnurlpubkey` -> `https://.grid.lightspark.com/.well-known/lnurlpubkey` - - `https:///.well-known/uma-configuration` -> `https://.grid.lightspark.com/.well-known/uma-configuration` - -Additionally, configure: -- Supported currencies (min/max, enabled transaction types) -- Required counterparty fields per currency for compliance screening - - -### UMA supported currencies -Define per‑currency rules for your UMA flows. Each entry can include: - -- `currencyCode`: (String, required) The ISO 4217 currency code (e.g., "USD"). -- `minAmount`: (Integer, required) Minimum transaction amount in the smallest unit of the currency. -- `maxAmount`: (Integer, required) Maximum transaction amount in the smallest unit of the currency. -- `requiredCounterpartyFields`: (Array, required) For regulated entities, defines PII your platform requires about *external counterparties* for transactions in this currency. -- `providerRequiredCustomerFields`: (Array, read-only) For regulated entities, lists user info field names (from `UserInfoFieldName`) that the UMA provider mandates for *your own users* to transact in this currency. This impacts user creation/updates. - -### Required counterparty fields - -For regulated entities, within each currency defined in `supportedCurrencies`, the `requiredCounterpartyFields` parameter allows you to specify what information your platform needs to collect from *external counterparties* (senders or receivers) involved in transactions with your users for that specific currency. - -Available counterparty fields (to be specified with a `name` and `mandatory` flag): - -| Field Name (type `UserInfoFieldName`) | Description | -|---------------------------------------|-------------| -| `FULL_NAME` | Full legal name of the individual or business | -| `BIRTH_DATE` | Date of birth in YYYY-MM-DD format (for individuals) | -| `NATIONALITY` | Nationality of the individual | -| `ADDRESS` | Physical address including country, city, etc. | -| `PHONE_NUMBER` | Contact phone number including country code | -| `EMAIL` | Email address | -| `BUSINESS_NAME` | Legal business name (for business entities) | -| `TAX_ID` | Tax identification number | - -Each field in `requiredCounterpartyFields` is an object containing: - -- `name`: The `UserInfoFieldName` representing the PII you require. -- `mandatory`: A boolean (true/false) indicating if this field is strictly required by your platform for transactions in this currency. - -This information will be provided to your platform via webhooks for pending payments, allowing you to screen the counterparty based on your compliance rules before approving the payment. - -### UMA provider required user fields -For regulated financial institutions, the `providerRequiredCustomerFields` array (per currency) lists the user fields required by the underlying provider. This array is read‑only and informs what you must capture for your own users. - -This list specifies which user information fields are mandated by the underlying UMA provider for *your own registered users* if they intend to send or receive payments in that particular currency. For example, to allow a user to transact in "USD", the UMA provider might require that the user has a `NATIONALITY` on record. - -These fields must be supplied when creating or updating a user via the `POST /customers` or `PATCH /customers/{customerId}` endpoints if that user is expected to use the specified currency. Refer to the [Configuring Customers](/global-p2p/onboarding-customers/configuring-customers) guide for more details on how this impacts user setup. - -## Manage configuration via API -If you prefer to manage settings programmatically, use the `/config` endpoints. - -### Retrieve current configuration - -You can retrieve your current platform configuration to see what settings are already in place: - -```bash -curl -sS -X GET "https://api.lightspark.com/grid/2025-10-13/config" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -Response example: - -```json -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "umaDomain": "example.com", - "webhookEndpoint": "https://api.example.com/webhooks/uma", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "requiredCounterpartyFields": [ - { - "name": "FULL_NAME", - "mandatory": true - }, - { - "name": "BIRTH_DATE", - "mandatory": true - } - ], - "providerRequiredCustomerFields": [ - "NATIONALITY", - "FULL_NAME" - ] - } - ], - "createdAt": "2023-06-15T12:30:45Z", - "updatedAt": "2023-07-01T10:00:00Z" -} -``` - -If this is your first time configuring the platform, some default values may be returned which were set up when you first created your account. - -### Update platform configuration - -To update your platform configuration, call the PATCH endpoint with the fields you want to change: - -```bash -curl -sS -X PATCH "https://api.lightspark.com/grid/2025-10-13/config" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "umaDomain": "mycompany.com", - "webhookEndpoint": "https://api.mycompany.com/webhooks/uma", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "requiredCounterpartyFields": [ - { "name": "FULL_NAME", "mandatory": true }, - { "name": "BIRTH_DATE", "mandatory": true }, - { "name": "ADDRESS", "mandatory": false } - ] - } - ] - }' -``` - -Response: - -```json -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "umaDomain": "mycompany.com", - "webhookEndpoint": "https://api.mycompany.com/webhooks/uma", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "requiredCounterpartyFields": [ - { - "name": "FULL_NAME", - "mandatory": true - }, - { - "name": "BIRTH_DATE", - "mandatory": true - }, - { - "name": "ADDRESS", - "mandatory": false - } - ], - "providerRequiredCustomerFields": [ - "NATIONALITY", - "FULL_NAME" - ] - } - ], - "createdAt": "2023-06-15T12:30:45Z", - "updatedAt": "2023-06-15T12:30:45Z" -} -``` - - - - -## Verify Configuration -After updating your configuration, it's recommended to verify that the changes were saved correctly: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/config" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -The response should reflect your updated **settings**. - - diff --git a/mintlify/global-p2p/index.mdx b/mintlify/global-p2p/index.mdx deleted file mode 100644 index af2e8222..00000000 --- a/mintlify/global-p2p/index.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: "Introduction" -description: "Overview of Global P2P: capabilities, flows" ---- -import { remittanceProductName, topLevelProductName } from '/snippets/variables.mdx'; -import PaymentFlow from '/snippets/payment-flow.mdx'; - -With {remittanceProductName}, you can send and receive low cost real-time payments to bank accounts and UMA addresses worldwide through a single, simple API. {topLevelProductName} automatically routes each payment across its network of Grid switches, handling FX, blockchain settlement, and instant banking off-ramps for you. - - - - {remittanceProductName} interacts with the Money Grid to route your payments globally. - - - Leverages local instant banking rails and global low latency crypto rails to settle payments in real-time. - - - Leverages local instant banking rails and global low latency crypto rails to settle payments in real-time. - - - -For background on the UMA protocol itself, see the UMA Standard documentation: [UMA Standard—Introduction](https://docs.uma.me/uma-standard/introduction). - - - -## {remittanceProductName} Features -Users interact with {remittanceProductName} through two main interfaces. - - - - Programmatic access to create customers, quotes, fund the account, send payments and reconcile via webhooks. - - - Your development and operations team can use the dashboard to monitor payments and webhooks, manage API keys and environments, and troubleshoot with logs. - - - -{remittanceProductName} supports the following functionality. - -### Onboarding customers -{remittanceProductName} has two customer onboarding options - one for non regulated entities where {remittanceProductName} handles the KYC/KYB process and one for regulated entities where you handle the KYC/KYB process. - -When creating customers, you'll be able to assign a customized UMA address to facilitate sending and receiving UMA payments. - -### Funding Payments -{remittanceProductName} supports multiple transaction funding options including prefunded accounts and real-time funding. You can prefund an account using several payment rails such as ACH, SEPA Instant, wire transfers, Lightning, and more. - -With real-time funding, you'll receive payment instructions as part of the quote. Once payment is received by our services, we'll initiate the payment to the receiver. - -### Sending & Receiving Payments -To send with {remittanceProductName}, you query recipient information and pricing, then execute and fund a quote. {remittanceProductName} resolves the receiver (by UMA or external bank details), returns min/max and an exchange rate, and provides funding instructions. Once funded, {remittanceProductName} handles FX and delivery to the receiving account. - -To receive with {remittanceProductName}, you expose an UMA or supported account identifier. The platform handles conversion and offramping to the receiver’s account currency and notifies you via webhooks so you can credit the customer. - -### Environments -{remittanceProductName} supports two environments: **Sandbox** and **Production**. - -Sandbox mirrors production behavior and webhooks so you can test receiver resolution, quotes and funding instructions, settlement status changes, and full end‑to‑end flows without moving real funds. - -Production uses live credentials and base URLs for real payments once you’re ready to launch. diff --git a/mintlify/global-p2p/managing-accounts/external-accounts.mdx b/mintlify/global-p2p/managing-accounts/external-accounts.mdx deleted file mode 100644 index 9577affa..00000000 --- a/mintlify/global-p2p/managing-accounts/external-accounts.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "External Accounts" -description: "Register and manage beneficiary bank accounts" ---- - -import ExternalAccounts from '/snippets/external-accounts.mdx'; - - - - diff --git a/mintlify/global-p2p/managing-accounts/internal-accounts.mdx b/mintlify/global-p2p/managing-accounts/internal-accounts.mdx deleted file mode 100644 index b787d2e8..00000000 --- a/mintlify/global-p2p/managing-accounts/internal-accounts.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Internal Accounts" -description: "Create and manage internal accounts" ---- - -import InternalAccounts from '/snippets/internal-accounts.mdx'; - - - - diff --git a/mintlify/global-p2p/managing-accounts/plaid.mdx b/mintlify/global-p2p/managing-accounts/plaid.mdx deleted file mode 100644 index 29cc23e5..00000000 --- a/mintlify/global-p2p/managing-accounts/plaid.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "External Accounts with Plaid" -description: "Simplify bank account linking with Plaid" ---- - -import PlaidIntegration from '/snippets/plaid-integration.mdx'; - - - - diff --git a/mintlify/global-p2p/onboarding-customers/configuring-customers.mdx b/mintlify/global-p2p/onboarding-customers/configuring-customers.mdx deleted file mode 100644 index e6805e3b..00000000 --- a/mintlify/global-p2p/onboarding-customers/configuring-customers.mdx +++ /dev/null @@ -1,281 +0,0 @@ ---- -title: "Configuring Customers" -description: "Creating and managing customers for global P2P payments" ---- -import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx' -import CustomerTypes from '/snippets/creating-customers/customer-types.mdx' -import OnboardingModel from '/snippets/creating-customers/onboarding-model.mdx' -import KycRegulated from '/snippets/kyc/kyc-regulated.mdx' -import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx' - -This guide provides comprehensive information about creating customers in the Grid API, including customer types, onboarding models, registration, and management. - - - - - -The base required information for all customers is only: - -- Platform customer ID (your internal identifier) -- Customer type (`INDIVIDUAL` or `BUSINESS`) - -If using sending and receiving to just-in-time UMA addresses, you'll also need to specify the bank account information - -## Creating Customers - - - - - - - - - - - -### Individual customers - -In some cases, only the above fields are required at customer creation. Beyond those base requirements, additional fields commonly associated with individual customers include: - -- Full name -- Date of birth (YYYY-MM-DD format) -- Physical address (including country, state, city, postalCode) - -**Note:** Check the `providerRequiredCustomerFields` for each relevant currency in your platform's configuration to determine which of these fields are strictly mandatory at creation/update time for that customer to transact in those currencies. - -### Business customers - -Beyond the base requirements, additional fields commonly associated with business customers include: - -- Business information: - - Legal name (this is often required, check `providerRequiredCustomerFields`) - - Registration number (optional, unless specified by `providerRequiredCustomerFields`) - - Tax ID (optional, unless specified by `providerRequiredCustomerFields`) -- Physical address (including country, state, city, postalCode) - -**Note:** Check the `providerRequiredCustomerFields` for each relevant currency in your platform's configuration to determine which of these fields are strictly mandatory at creation/update time for that customer to transact in those currencies. - -When creating or updating customers, the `customerType` field must be specified as either `INDIVIDUAL` or `BUSINESS`. - -There can be multiple customers with the same platformCustomerId but different UMA addresses. This is useful if you want to track multiple UMA addresses and/or bank accounts for the same customer in your platform. - - -## Customer Creation Process - - ### Creating a new individual customer (regulated institutions) - -To register a new customer directly, use the `POST /customers` endpoint (regulated institutions): - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "platformCustomerId": "9f84e0c2a72c4fa", - "customerType": "INDIVIDUAL", - "fullName": "Jane Doe", - }' -``` - -The API allows creating a customer with minimal PII. However, to enable transactions for a customer in specific currencies, you must include any PII fields mandated by the `providerRequiredCustomerFields` for those currencies (found in your platform's configuration via `GET /config`). - - -The examples below show a more comprehensive set of data. Not all fields are strictly required by the API for customer creation itself, but become necessary based on currency and provider requirements. - -Example request body for an individual customer with UMA instant payments enabled (ensure all `providerRequiredCustomerFields` for intended currencies are included): - -Typically bank account information is provided separately via internal and external account management. However, when using UMA for instant payments, since funding and withdrawals are instant, bank account information can be provided at time of customer creation. - - -```json -{ - "umaAddress": "$john.sender@mycompany.com", - "platformCustomerId": "9f84e0c2a72c4fa", - "customerType": "INDIVIDUAL", - "fullName": "John Sender", - "birthDate": "1985-06-15", - "address": { - "line1": "Paseo de la Reforma 222", - "line2": "Piso 15", - "city": "Ciudad de México", - "state": "Ciudad de México", - "postalCode": "06600", - "country": "MX" - } -} -``` - -UMA addresses follow the format `$username@domain`. For your platform: - -1. The `domain` part will be your configured UMA domain (set in platform configuration) -2. The `username` part can be chosen by you or your customers, following these rules: - - Must start with a $ symbol. This is to differentiate from email addresses and clearly identify an uma address. - - The `username` portion is limited to a-z0-9-_.+ - - Addresses are case-insensitive, but by convention are written only with lowercase letters - - Like email addresses, the maximum number of characters for the `username` portion of the address is 64 characters (including the $). - -The Grid API validates these requirements and will return an error if they are not met. - - -### Creating a new business customer (regulated institutions) - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "umaAddress": "$acme.corp@mycompany.com", - "platformCustomerId": "b87d2e4a9c13f5b", - "customerType": "BUSINESS", - "businessInfo": { - "legalName": "Acme Corporation", - "registrationNumber": "789012345", - "taxId": "123-45-6789" - }, - "address": { - "line1": "456 Oak Avenue", - "line2": "Floor 12", - "city": "New York", - "state": "NY", - "postalCode": "10001", - "country": "US" - } - }' -``` - -### Onboarding customers (unregulated institutions) - -Unregulated institutions should initiate a hosted KYC/KYB flow. Generate a link and redirect the customer to complete verification. While KYC is pending, allow account setup but block funding and money movement. - -1. Request a hosted KYC link for a customer using your `platformCustomerId` (optional `redirectUri` to return the user to your app when finished): - -```bash -curl -sS -G "https://api.lightspark.com/grid/2025-10-13/customers/kyc-link" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - --data-urlencode "platformCustomerId=9f84e0c2a72c4fa" \ - --data-urlencode "redirectUri=https://app.example.com/onboarding/completed" -``` - -Response: -```json -{ - "kycUrl": "https://kyc.grid.example/onboard/abc123", - "platformCustomerId": "9f84e0c2a72c4fa", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001" -} -``` - -2. Redirect the customer to `kycUrl` to complete KYC/KYB in their locale. -3. After the user is redirected back to your app, they can continue with account setup until KYC review is complete. -4. Handle the KYC status webhook. Grid notifies you when a decision is reached; update your records and unlock funding on APPROVED. - -### Handling KYC/KYB Webhooks - - - -## Customer management - -### Retrieving customer information - -You can retrieve customer information using either the Grid-assigned customer ID or your platform's customer ID: - -```bash -curl -sS -X GET "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -or list customers with a filter: - -```bash -curl -sS -G "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - --data-urlencode "umaAddress={umaAddress}" \ - --data-urlencode "platformCustomerId={platformCustomerId}" \ - --data-urlencode "customerType={customerType}" \ - --data-urlencode "createdAfter={createdAfter}" \ - --data-urlencode "createdBefore={createdBefore}" \ - --data-urlencode "cursor={cursor}" \ - --data-urlencode "limit={limit}" -``` - -Note that this example shows all available filters. You can use any combination of them. - -## Data validation - -The Grid API performs validation on all customer data. Common validation rules include: - -- All required fields must be present based on customer type -- Date of birth must be in YYYY-MM-DD format and represent a valid date -- Names must not contain special characters -- Bank account information must follow country-specific formats -- Addresses must include all required fields including country code - -If validation fails, the API will return a 400 Bad Request response with detailed error information. - -## Bulk customer import operations - -For scenarios where you need to add many customers to the system at once, the API provides a CSV file upload endpoint. - -### CSV file upload - -For large-scale customer imports, you can upload a CSV file containing customer information: - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers/bulk/csv" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -F "file=@customers.csv" -``` - -The CSV file should follow a specific format with required and optional columns based on customer type. Here's an example: - -```csv -umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,accountType,accountNumber,bankName,platformAccountId,businessLegalName,routingNumber,accountCategory -$john.doe@uma.domain.com,cust_user123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US,US_ACCOUNT,123456789,Chase Bank,chase_primary_1234,,222888888,SAVINGS -$acme@uma.domain.com,cust_biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US,US_ACCOUNT,987654321,Bank of America,boa_business_5678,Acme Corp,121212121,CHECKING -``` - - -CSV Upload Best Practices - -1. Use a spreadsheet application to prepare your CSV file -2. Validate data before upload (e.g., date formats, required fields) -3. Include a header row with column names -4. Use UTF-8 encoding for special characters -5. Keep file size under 100MB for optimal processing - - -You can track the job status through: - -1. Webhook notifications (if configured) -2. Status polling endpoint: - -```bash -curl -sS -X GET "https://api.lightspark.com/grid/2025-10-13/customers/bulk/jobs/{jobId}" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -Example job status response: - -```json -{ - "jobId": "job_123456789", - "status": "PROCESSING", - "progress": { - "total": 5000, - "processed": 2500, - "successful": 2499, - "failed": 1 - }, - "errors": [ - { - "platformCustomerId": "cust_biz456", - "error": { - "code": "validation_error", - "message": "Invalid bank account number" - } - } - ] -} -``` - - diff --git a/mintlify/global-p2p/onboarding-customers/invitations.mdx b/mintlify/global-p2p/onboarding-customers/invitations.mdx deleted file mode 100644 index bc5f0a85..00000000 --- a/mintlify/global-p2p/onboarding-customers/invitations.mdx +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: "Invitations" ---- - -The Grid API provides an invitation system that allows platform customers to invite others to create accounts and receive payments. This guide explains how to use the invitation system effectively. - - See the full [UMA invitations guide](https://docs.lightspark.com/uma-invitations/introduction) for details outside of the context of the Grid API. - -## Overview - -The invitation system enables: - -- Platform users to create invitations with optional payments for others -- Direct prospects to sign up for your services by claiming an invitation to receive payment -- Tracking of invitation status (pending, claimed, expired) -- Webhook notifications when invitations are claimed - -## Setup - -Before starting your implementation, we recommend configuring UMAs and invitations in the dashboard. - -You will need to provide: - -- A name and a logo to be displayed in the invitation page. -- A list of countries you operate UMA in. -- An onboarding URL for people who want to create an account with you (this URL must support invitation codes). Example: `http://myplatform.com/uma?code=INVITATION_CODE` -- A webhook URL to get notified when your invitations get claimed. - -## Creating Invitations - -To create an invitation, make a POST request to `/invitations` with the inviter's UMA address: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/invitations" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "inviterUma": "$inviter@uma.domain", - "expiresAt": "2024-12-31T23:59:59Z" - }' -``` - -The response will include a unique invitation code that can be shared with the invitee: - -```json -{ - "code": "019542f5", - "createdAt": "2023-09-01T14:30:00Z", - "inviterUma": "$inviter@uma.domain", - "url": "https://uma.me/i/019542f5", - "status": "PENDING" -} -``` - -The `url` field is the URL where the invitee can claim the invitation. For example, for the response above, you might want to generate a share message -for the user with text like: "Get an UMA address so that I can send you some money! `https://uma.me/i/019542f5`". The inviter can then share this URL with the invitee. - -When the invitee clicks the URL, they will be presented with a list of UMA providers available in their region. The invitee can select one of the providers and onboard to create their UMA address. - -## Pay-by-Link Invitations - -The Grid API supports a "pay-by-link" feature that allows users to create invitations that include a payment amount. This is useful for scenarios where you want to send money to someone who doesn't yet have an UMA address or where the sender doesn't know the receiver's UMA address. They can simply share a link via email, SMS, whatsapp, or other channels to send money. - -To create a pay-by-link invitation, include the `amountToSend` field when creating the invitation: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/invitations" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "inviterUma": "$inviter@uma.domain", - "amountToSend": 5000, - "expiresAt": "2024-12-31T23:59:59Z" - }' -``` - -Assuming the user's currency is USD, this example creates an invitation that will send $50 USD to the invitee when -they claim it. The response will include the payment amount in the invitation details: - -```json -{ - "code": "019542f5", - "createdAt": "2023-09-01T14:30:00Z", - "inviterUma": "$inviter@uma.domain", - "url": "https://uma.me/i/019542f5", - "status": "PENDING", - "amountToSend": { - "amount": 5000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - } -} -``` - -When the invitee claims the invitation, your platform will receive an `INVITATION_CLAIMED` webhook. At this point, you should: - -1. Check the `amountToSend` field in the webhook payload -2. Create a quote for the payment amount (sender-locked) -3. Execute the payment to the invitee's UMA address - -Note that the actual sending of the payment must be done by your platform after receiving the webhook. If your platform either does not send the payment or the payment fails, the invitee will not receive the amount. The `amountToSend` field is primarily used for display purposes on the claiming side of the invitation. - -These payments can only be sender-locked, meaning that the sender will not know ahead of time how much the receiver will receive in their local currency. The exchange rate will be determined at the time the payment is executed. If you'd like, you can also send a push notification to your sending user when you receive the `INVITATION_CLAIMED` webhook and have them approve the payment interactively instead. - -### Best practices - -1. Always set an expiration time for invitations with a payment amount to avoid huge swings in expected exchange rates or leaked links. -2. Allow the inviter to cancel the invitation if they want to avoid sending a payment to the wrong person if the link is leaked. -3. Notify the inviter when the invitation is claimed so that they can see the amount received by the invitee. - -## Claiming Invitations - -Once onboarding (or login from an invite link) is complete, the invitee's new VASP (which may or may not be the same as the inviter's Grid API platform) will need to claim the invitation by making a -POST request to `/invitations/{invitationCode}/claim` including the invitee's newly-created UMA address: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/invitations/019542f5/claim" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "inviteeUma": "$invitee@uma.domain" - }' -``` - -A successful claim will: - -1. Associate the invitee's UMA address with the invitation -2. Change the invitation status from `PENDING` to `CLAIMED` -3. Trigger an `INVITATION_CLAIMED` webhook to notify the inviter's platform of the claim - -## Cancelling Invitations - -To cancel a pending invitation, make a POST request to `/invitations/{invitationCode}/cancel`: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/invitations/019542f5/cancel" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -A successful cancellation will: - -1. Change the invitation status from `PENDING` to `CANCELLED` -2. Make the invitation URL show as cancelled when accessed -3. Prevent the invitation from being claimed - -Only the inviter or platform can cancel an invitation, and only pending invitations can be cancelled. Attempting to cancel an invitation that is already claimed, expired, or cancelled will result in an error. - -Example error response for an already claimed invitation: - -```json -{ - "code": "invitation_already_claimed", - "message": "This invitation has already been claimed and cannot be cancelled", - "details": { - "status": "CLAIMED", - "inviteeUma": "$invitee@uma.domain" - } -} -``` - -## Invitation Status - -An invitation can be in one of four states: - -- `PENDING`: The invitation has been created but not yet claimed -- `CLAIMED`: The invitation has been successfully claimed by an invitee -- `EXPIRED`: The invitation has expired and can no longer be claimed -- `CANCELLED`: The invitation has been cancelled by the inviter or platform - -You can check the status of an invitation at any time by making a GET request to `/invitations/{invitationCode}`. - -## Webhook Integration - -When an invitation is claimed, the Grid API will send an `INVITATION_CLAIMED` webhook to your configured webhook endpoint. This allows you to: - -- Track invitation usage and conversion rates -- Apply referral bonuses or rewards to the inviter -- Update your UI to reflect the claimed status - -Example webhook payload: - -```json -{ - "invitation": { - "code": "019542f5", - "createdAt": "2023-09-01T14:30:00Z", - "claimedAt": "2023-09-01T15:45:00Z", - "inviterUma": "$inviter@uma.domain", - "inviteeUma": "$invitee@uma.domain", - "url": "https://uma.me/i/019542f5", - "status": "CLAIMED" - }, - "timestamp": "2023-09-01T15:45:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000008", - "type": "INVITATION_CLAIMED" -} -``` - -See the [Webhooks Guide](../platform-tools/webhooks) for more information about webhook security and implementation. - -## Best Practices - -1. **Expiration Times**: Consider setting appropriate expiration times for invitations based on your use case -2. **User Experience**: Provide clear feedback to users about invitation status and next steps -3. **Monitoring**: Track invitation metrics to understand user acquisition patterns - -## Error Handling - -Common error scenarios to handle: - -- Invalid invitation code -- Expired invitation -- Already claimed invitation -- Rate limit exceeded -- Missing required fields - -Example error response: - -```json -{ - "code": "invitation_expired", - "message": "This invitation has expired and cannot be claimed", - "details": { - "expirationTime": "2023-08-31T23:59:59Z" - } -} -``` - - diff --git a/mintlify/global-p2p/platform-tools/postman-collection.mdx b/mintlify/global-p2p/platform-tools/postman-collection.mdx deleted file mode 100644 index cfaee6ea..00000000 --- a/mintlify/global-p2p/platform-tools/postman-collection.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "Postman Collection" ---- - -import PostmanCollection from '/snippets/postman-collection.mdx'; - - - - - - diff --git a/mintlify/global-p2p/platform-tools/sandbox-testing.mdx b/mintlify/global-p2p/platform-tools/sandbox-testing.mdx deleted file mode 100644 index ec4fe573..00000000 --- a/mintlify/global-p2p/platform-tools/sandbox-testing.mdx +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: "Sandbox Testing" ---- -The Grid sandbox environment allows you to test your integration without making real payments. When you set up your account, you can configure production and sandbox API tokens. The sandbox token is specifically for testing and development purposes. -It corresponds to a separate platform instance in "sandbox" mode, which can only transact with the sandbox UMA addresses for testing. - -## Overview - -The sandbox environment provides: - -1. A dedicated sandbox platform for testing -2. Test UMA addresses for simulating payments -3. Endpoints to simulate sending and receiving payments -4. All the same webhooks and flows as production, but with simulated funds - -## Test UMA Addresses - -The sandbox provides several test UMA addresses you can use to simulate different scenarios: - -| UMA Address | Description | -|------------|-------------| -| `$success.usd@sandbox.uma.money` | Always succeeds, sends USD | -| `$success.eur@sandbox.uma.money` | Always succeeds, sends EUR | -| `$success.mxn@sandbox.uma.money` | Always succeeds, sends MXN | -| `$pending.long.usd@sandbox.uma.money` | Simulates a long-pending payment | -| `$fail.compliance.usd@sandbox.uma.money` | Simulates compliance check failure | - -## Testing Outgoing Payments - -To test sending payments from your platform, follow these steps: - -```mermaid -sequenceDiagram - participant Client as Your Platform - participant Grid as Grid Sandbox - participant Test as Test UMA Address - - Note over Client, Grid: Testing Outgoing Payments - Client->>Grid: GET /receiver/$success.usd@sandbox.uma.money - Grid-->>Client: Supported currencies and requirements - Client->>Grid: POST /quotes - Grid-->>Client: Quote with payment instructions - Client->>Grid: POST /sandbox/send - Grid-->>Client: Payment simulated - Grid->>Client: Webhook: OUTGOING_PAYMENT (COMPLETED) - - Note over Client, Grid: Testing Incoming Payments - Client->>Grid: POST /sandbox/uma/receive - Grid->>Client: Webhook: INCOMING_PAYMENT (PENDING) - Client-->>Grid: HTTP 200 OK (approve payment) - Grid->>Client: Webhook: INCOMING_PAYMENT (COMPLETED) -``` - -1. Look up a sandbox UMA address: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/receiver/\$success.usd@sandbox.uma.money" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -2. Create a quote as normal: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009", - "sendingCurrencyCode": "MXN", - "receivingCurrencyCode": "USD", - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000 - }' -``` - -3. Instead of making a real bank transfer, use the sandbox send endpoint: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/send" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "reference": "UMA-Q12345-REF", - "currencyCode": "USD", - "currencyAmount": 10000 - }' -``` - -The sandbox will simulate the payment and send appropriate webhooks just like in production. - -## Testing Incoming Payments - -To test receiving payments to your platform's users, use the sandbox receive endpoint: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/uma/receive" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "senderUmaAddress": "$success.usd@sandbox.uma.money", - "receiverUmaAddress": "$your.user@your.domain", - "receivingCurrencyCode": "USD", - "receivingCurrencyAmount": 5000 - }' -``` - -This will trigger the same webhook flow as a real incoming payment: - -1. You'll receive an `INCOMING_PAYMENT` webhook with `status: "PENDING"` -2. Your platform should approve/reject the payment -3. On approval, you'll receive another webhook with `status: "COMPLETED"` - -## Example Testing Flow - -Here's a complete example of testing both directions of payments: - -1. First, register a test user: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/users" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "umaAddress": "$test.user@your.domain", - "platformUserId": "test_123", - "userType": "INDIVIDUAL", - "fullName": "Test User", - "birthDate": "1990-01-01", - "address": { - "line1": "123 Test St", - "city": "Testville", - "state": "TS", - "postalCode": "12345", - "country": "US" - } - }' -``` - -2. Test receiving a payment: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/uma/receive" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "senderUmaAddress": "$success.usd@sandbox.uma.money", - "receiverUmaAddress": "$test.user@your.domain", - "receivingCurrencyCode": "USD", - "receivingCurrencyAmount": 5000 - }' -``` - -3. Test sending a payment: - -```bash -# 1. Look up recipient -curl -X GET "https://api.lightspark.com/grid/2025-10-13/receiver/\$success.usd@sandbox.uma.money" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" - -# 2. Create quote -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009", - "sendingCurrencyCode": "MXN", - "receivingCurrencyCode": "USD", - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000 - }' - -# 3. Simulate sending payment -curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/send" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "reference": "UMA-Q12345-REF", - "currencyCode": "USD", - "currencyAmount": 10000 - }' -``` - -## Testing Error Scenarios - -You can test various error scenarios using the special sandbox UMA addresses: - -1. Test compliance failures: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/receiver/\$fail.compliance.usd@sandbox.uma.money" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -# ... create quote and attempt payment -``` - -2. Test long-pending payments: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/receiver/\$pending.long.usd@sandbox.uma.money" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -# ... create quote and attempt payment -``` - -3. Non-existent UMA address: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/receiver/\$non.existent.usd@sandbox.uma.money" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -# ... should return 404 Not Found -``` - -Each of these will trigger appropriate error webhooks and status updates to help you test your error handling. - -## Production vs Sandbox - -Here are the key differences between production and sandbox environments: - -1. **API Tokens**: Sandbox tokens only work in the sandbox environment and vice versa -2. **Bank Transfers**: In sandbox, you use `/sandbox/send` instead of real bank transfers -3. **Test UMA Addresses**: Special sandbox addresses for testing different scenarios -4. **Money**: No real money is moved in sandbox - -Always test thoroughly in sandbox before moving to production! - - diff --git a/mintlify/global-p2p/platform-tools/uma-test-wallet.mdx b/mintlify/global-p2p/platform-tools/uma-test-wallet.mdx deleted file mode 100644 index 20903dae..00000000 --- a/mintlify/global-p2p/platform-tools/uma-test-wallet.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "UMA Test Wallet" -description: "Test UMA payment flows with a real counterparty" ---- - -Grid provides an UMA Test Wallet to help you test UMA payment flows with a real counterparty. - -import UMATestWallet from '/snippets/uma-test-wallet.mdx'; - - - - diff --git a/mintlify/global-p2p/platform-tools/webhooks.mdx b/mintlify/global-p2p/platform-tools/webhooks.mdx deleted file mode 100644 index 5ed3d984..00000000 --- a/mintlify/global-p2p/platform-tools/webhooks.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Webhooks" -description: "Security best practices for webhook verification" ---- - -import Webhooks from '/snippets/webhooks.mdx'; - - - - diff --git a/mintlify/global-p2p/quickstart.mdx b/mintlify/global-p2p/quickstart.mdx deleted file mode 100644 index 5c5483dc..00000000 --- a/mintlify/global-p2p/quickstart.mdx +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: "Quickstart" -description: "Send your first cross-border payment" ---- -import { remittanceProductName } from '/snippets/variables.mdx' - -With Global P2P you can send and receive payments in any supported fiat or crypto currency. This quickstart guides you through a regulated FI sending an individual customer payment from the US to a bank account in Mexico using USDC just-in-time funding. For examples funding with real time fiat rails, see the [Sending Payments](/global-p2p/sending-receiving-payments/sending-payments) guide. - -## Understanding Entity Mapping for Remittances - -In this guide, the entities map as follows: - -| Entity Type | Who They Are | In This Example | -|------------|--------------|----------------| -| **Platform** | Your remittance service | Your money transfer app | -| **Customer** | Both sender and recipient | Alice (sender in US), Carlos (recipient in Mexico) | -| **External Account** | Bank accounts for funding/receiving | Alice's US bank (funding), Carlos's Mexican bank (receiving funds) | - -**Flow**: Alice (customer) funds a transfer from her external account → to Carlos (also a customer) → who receives funds in his external bank account. Alternatively, Alice could send directly to Carlos's UMA address if he has one. - -## Get API credentials - Create a Sandbox API credentialsin the dashboard, then set environment variables for local use. - - ```bash - export GRID_BASE_URL="https://api.lightspark.com/grid/2025-10-13" - export GRID_CLIENT_ID="YOUR_SANDBOX_CLIENT_ID" - export GRID_CLIENT_SECRET="YOUR_SANDBOX_CLIENT_SECRET" - ``` - - - Use Basic Auth in cURL with `-u "$GRID_CLIENT_ID:$GRID_API_SECRET"`. - -## Create a customer - Register a customer who will send the payment. You can provide your own UMA handle or let the system generate one. - - ```bash - curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "platformCustomerId": "cust_7b3c5a89d2f1e0", - "customerType": "INDIVIDUAL", - "umaAddress": "$alice@yourapp.example", - "fullName": "Alice Smith", - "address": { - "line1": "123 Pine Street", - "city": "Seattle", - "state": "WA", - "postalCode": "98101", - "country": "US" - } - }' - ``` - - Response: - ```json - { - "id": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "customerType": "INDIVIDUAL", - "umaAddress": "$alice@yourapp.example", - "platformCustomerId": "cust_7b3c5a89d2f1e0" - } - ``` -## Create an external receiving bank account (CLABE in MX) - Add a beneficiary account in Mexico using their CLABE. We attach it to the same customer for this example. - - ```bash - curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "currency": "MXN", - "platformAccountId": "mx_beneficiary_001", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "accountInfo": { - "accountType": "CLABE", - "clabeNumber": "123456789012345678", - "bankName": "BBVA Mexico", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Carlos Pérez", - "birthDate": "1985-03-15", - "nationality": "MX", - "address": { - "line1": "Av. Reforma 123", - "city": "Ciudad de México", - "state": "CDMX", - "postalCode": "06600", - "country": "MX" - } - } - } - }' - ``` - - Response: - ```json - { - "id": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "MXN", - "status": "ACTIVE", - "platformAccountId": "mx_beneficiary_001", - "accountInfo": { - "accountType": "CLABE", - "clabeNumber": "123456789012345678", - "bankName": "BBVA Mexico", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Carlos Pérez", - "birthDate": "1985-03-15", - "nationality": "MX", - "address": { - "line1": "Av. Reforma 123", - "city": "Ciudad de México", - "state": "CDMX", - "postalCode": "06600", - "country": "MX" - } - } - } - } - ``` -## Create a quote (just‑in‑time funding) - Quote a transfer of 100 USD from the customer to the MXN CLABE account. The quote returns the exchange rate, fees, and `paymentInstructions` to fund in real time. - - ```bash - curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USDC" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "MXN" - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Remittance to MX beneficiary" - }' - ``` - - Response: - ```json - { - "id": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "exchangeRate": 16.85, - "fees": { "amount": 50, "currency": { "code": "USD", "decimals": 2 } }, - "paymentInstructions": [ - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "address": "0x1234567890123456789012345678901234567890", - "assetType": "USDC" - } - }, - { - "accountOrWalletInfo": { - "accountType": "BASE_WALLET", - "address": "0x1234567890123456789012345678901234567890", - "assetType": "USDC" - } - } - ], - "status": "PENDING" - } - ``` -## Fund the quote (Sandbox simulation) - In production, you would trigger a payment on one of the supported blockchains to the provided address. In Sandbox, you can mock funding using the simulate send endpoint. - - ```bash - curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/send" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "reference": "UMA-Q12345-REF", - "currencyCode": "USDC", - "currencyAmount": 10000 - }' - ``` - - Response: - ```json - { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "PROCESSING", - "type": "OUTGOING", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006" - } - ``` -## Handle the outgoing payment webhook - Implement a webhook endpoint to receive status updates as the payment moves from pending to completed (or failed). Verify the `X-Grid-Signature` header using the public key provided during onboarding. - - Webhook event: - ```json - { - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "COMPLETED", - "type": "OUTGOING", - "senderUmaAddress": "$alice@yourapp.example", - "receivedAmount": { "amount": 9706, "currency": { "code": "MXN", "decimals": 2 } }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - }, - "timestamp": "2025-01-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "OUTGOING_PAYMENT" - } - ``` - diff --git a/mintlify/global-p2p/sending-receiving-payments/depositing-funds.mdx b/mintlify/global-p2p/sending-receiving-payments/depositing-funds.mdx deleted file mode 100644 index 6bc5b142..00000000 --- a/mintlify/global-p2p/sending-receiving-payments/depositing-funds.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Depositing Funds" -description: "Depositing funds into internal accounts" ---- - -import DepositingFunds from '/snippets/depositing-funds.mdx'; - - - - diff --git a/mintlify/global-p2p/sending-receiving-payments/error-handling.mdx b/mintlify/global-p2p/sending-receiving-payments/error-handling.mdx deleted file mode 100644 index 64b39856..00000000 --- a/mintlify/global-p2p/sending-receiving-payments/error-handling.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Error Handling" -description: "Handle payment failures, API errors, and transaction issues in Global P2P" ---- - -import ErrorHandling from '/snippets/error-handling.mdx' - - - - diff --git a/mintlify/global-p2p/sending-receiving-payments/receiving-payments.mdx b/mintlify/global-p2p/sending-receiving-payments/receiving-payments.mdx deleted file mode 100644 index 636ed38c..00000000 --- a/mintlify/global-p2p/sending-receiving-payments/receiving-payments.mdx +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: "Receiving Payments" -description: "Receiving payments from UMA addresses" ---- - -This guide explains how to enable your customers to receive UMA payments. When an external sender initiates a payment to your customer's UMA address, the Grid API requests the counterparty fields you defined in your platform configuration. You can review the counterparty and transaction details for risk before approving the payment. - - -Before you begin, make sure you: - -- Configure UMA, supported currencies, and required counterparty fields in Platform Configuration -- Create customers and capture any provider-required user fields in Creating Customers -- Set up and verify webhooks in Webhooks - - - - -## Receive webhook for initiated payment - -When someone initiates a payment to one of your users' UMA addresses, you'll receive a webhook call with a pending transaction: - -```json -{ - "transaction": { - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "PENDING", - "type": "INCOMING", - "senderUmaAddress": "$mary.sender@thelessgoodbank.com", - "receiverUmaAddress": "$john.receiver@thegoodbank.com", - "receivedAmount": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "userId": "User:019542f5-b3e7-1d02-0000-000000000001", - "platformUserId": "9f84e0c2a72c4fa", - "description": "Payment for services", - "counterpartyInformation": { - "FULL_NAME": "Mary Sender", - "BIRTH_DATE": "1985-06-15" - }, - "reconciliationInstructions": { - "reference": "REF-123456789" - } - }, - "requestedReceiverUserInfoFields": [ - { "name": "COUNTRY_OF_RESIDENCE", "mandatory": true }, - { "name": "FULL_NAME", "mandatory": true }, - { "name": "NATIONALITY", "mandatory": false } - ], - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "INCOMING_PAYMENT" -} -``` - -The `counterpartyInformation` object contains PII about the sender, provided by their FI, based on your configured `requiredCounterpartyFields`. If present, `requestedReceiverUserInfoFields` lists information needed about your user to proceed. Provide those fields when approving. - -You can approve or reject the payment synchronously (recommended) or asynchronously: - -### Option 1: Synchronous (recommended) - -To approve the payment synchronously, respond with a `200 OK` status: - -- If the `requestedReceiverUserInfoFields` array was present in the webhook request and contained mandatory fields, your `200 OK` response **must** include a JSON body containing a `receiverUserInfo` object. This object should contain the key-value pairs for the information fields that were requested. -- If `requestedReceiverUserInfoFields` was not present, was empty, or contained only non-mandatory fields for which you have no information, your `200 OK` response can have an empty body. - -Example `200 OK` response body when information was requested and provided: - -```json -{ - "receiverUserInfo": { - "COUNTRY_OF_RESIDENCE": "US", - "FULL_NAME": "John Receiver" - } -} -``` - -To reject the payment, respond with a 403 Forbidden status and a JSON body with the following fields: - -```json -{ - "code": "payment_rejected", - "message": "Payment rejected due to compliance policy", - "details": { - "reason": "failed_counterparty_check", - "rejectionReason": "User is in a restricted jurisdiction" - } -} -``` - -### Option 2: Asynchronous Processing - -If your platform's architecture requires asynchronous processing before approving or rejecting the payment, you can: - -1. Return a `202 Accepted` response to acknowledge receipt of the webhook -2. Process the payment asynchronously -3. Call either the `/transactions/{transactionId}/approve` or `/transactions/{transactionId}/reject` endpoint *within 5 seconds* - -Example of approving asynchronously: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000005/approve" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "receiverUserInfo": { - "COUNTRY_OF_RESIDENCE": "US", - "FULL_NAME": "John Receiver" - } - }' -``` - -Example of rejecting asynchronously: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000005/reject" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "reason": "RESTRICTED_JURISDICTION" - }' -``` - - -If you choose the asynchronous path, you must call the approve/reject endpoint within 5 seconds, or the payment will be automatically rejected. - - -## Receive completion notification and credit - -When the payment completes, you'll receive another webhook notifying you of the completion: - -```json -{ - "transaction": { - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "COMPLETED", - "type": "INCOMING", - "senderUmaAddress": "$mary.sender@thelessgoodbank.com", - "receiverUmaAddress": "$john.receiver@thegoodbank.com", - "receivedAmount": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "userId": "User:019542f5-b3e7-1d02-0000-000000000001", - "platformUserId": "9f84e0c2a72c4fa", - "settlementTime": "2023-08-15T14:30:00Z", - "createdAt": "2023-08-15T14:25:18Z", - "description": "Payment for services", - "counterpartyInformation": { - "FULL_NAME": "Mary Sender", - "BIRTH_DATE": "1985-06-15" - }, - "reconciliationInstructions": { - "reference": "REF-123456789" - } - }, - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "INCOMING_PAYMENT" -} -``` - -On completion, the received funds are immediately credited to the account associated with the UMA customer. By default, funds -land in the customer's primary internal account. However, you can also set an external account as the default UMA deposit account -for the customer, which will cause incoming UMA payments to be deposited into that external account instead. This is useful for -customers who prefer to receive UMA payments into a specific bank account directly, for example. - -To set an external account as the default UMA deposit account for a customer, you can set the `defaultUmaDepositAccount` flag to true when creating the external account. - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "9876543210", - "routingNumber": "123456789", - "accountCategory": "CHECKING", - "bankName": "Chase Bank" - }, - "defaultUmaDepositAccount": true - }' -``` - -## Test the inbound flow - -Use the UMA Test Wallet to send a regtest payment to your customer's UMA address. - - diff --git a/mintlify/global-p2p/sending-receiving-payments/reconciliation.mdx b/mintlify/global-p2p/sending-receiving-payments/reconciliation.mdx deleted file mode 100644 index 2ce924bb..00000000 --- a/mintlify/global-p2p/sending-receiving-payments/reconciliation.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Reconciliation" -description: "Reconciliation of payments" ---- -import Reconciliation from '/snippets/reconciliation/reconciliation.mdx' - - diff --git a/mintlify/global-p2p/sending-receiving-payments/sending-payments.mdx b/mintlify/global-p2p/sending-receiving-payments/sending-payments.mdx deleted file mode 100644 index 57c87cb9..00000000 --- a/mintlify/global-p2p/sending-receiving-payments/sending-payments.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "Sending Payments" ---- -This guide covers three methods to send payments: - -1. Same-currency transfer to an external account -2. Cross-currency transfer with a quote -3. Sending to an UMA address - -## Choosing the right method - -- **Same-currency**: Best for domestic payouts when sender and recipient use the same currency. Uses local payment rails (e.g., RTP, SEPA Instant, PIX, FPS) for low cost and fast settlement. -- **Cross-currency**: Use when conversion is required or when paying globally across borders. Also supports sending to a crypto wallet address when configured. -- **UMA**: Send using a Universal Money Address. Ideal for global counterparties on networks. - -import SameCurrency from '/snippets/sending/same-currency.mdx' - - - -import CrossCurrency from '/snippets/sending/cross-currency.mdx' - - - -import SendUMA from '/snippets/sending/uma.mdx' - - - - diff --git a/mintlify/global-p2p/terminology.mdx b/mintlify/global-p2p/terminology.mdx deleted file mode 100644 index 8e1a67ab..00000000 --- a/mintlify/global-p2p/terminology.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Core Concepts" -description: "Core concepts and terminology for the Grid API" ---- - -import Terminology from '/snippets/terminology.mdx'; - - - - diff --git a/mintlify/images/IconArrowTopBottom.svg b/mintlify/images/IconArrowTopBottom.svg deleted file mode 100644 index fa29ad42..00000000 --- a/mintlify/images/IconArrowTopBottom.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mintlify/images/IconChevronDoubleUp.svg b/mintlify/images/IconChevronDoubleUp.svg deleted file mode 100644 index 827aba53..00000000 --- a/mintlify/images/IconChevronDoubleUp.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mintlify/images/IconGift.svg b/mintlify/images/IconGift.svg deleted file mode 100644 index a58f3fcc..00000000 --- a/mintlify/images/IconGift.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mintlify/images/IconGlobus-green.svg b/mintlify/images/IconGlobus-green.svg deleted file mode 100644 index e31a8fe8..00000000 --- a/mintlify/images/IconGlobus-green.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mintlify/images/IconGlobus.svg b/mintlify/images/IconGlobus.svg deleted file mode 100644 index 1cf55294..00000000 --- a/mintlify/images/IconGlobus.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mintlify/images/IconHammer-white.svg b/mintlify/images/IconHammer-white.svg deleted file mode 100644 index 4d7d10b7..00000000 --- a/mintlify/images/IconHammer-white.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/mintlify/images/IconHammer.svg b/mintlify/images/IconHammer.svg deleted file mode 100644 index 5e2e11c6..00000000 --- a/mintlify/images/IconHammer.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/mintlify/images/blue-lines-sm.svg b/mintlify/images/blue-lines-sm.svg deleted file mode 100644 index 2f0c5606..00000000 --- a/mintlify/images/blue-lines-sm.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/mintlify/images/blue-lines.svg b/mintlify/images/blue-lines.svg deleted file mode 100644 index b7cc8e2c..00000000 --- a/mintlify/images/blue-lines.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/mintlify/images/entities.png b/mintlify/images/entities.png deleted file mode 100644 index 20b58e03e624b83128eb3ad8a28f4f40d3f36262..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121708 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 - - diff --git a/mintlify/images/github.svg b/mintlify/images/github.svg deleted file mode 100644 index b286bc7c..00000000 --- a/mintlify/images/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/mintlify/images/grid-white.svg b/mintlify/images/grid-white.svg deleted file mode 100644 index f6e4aa19..00000000 --- a/mintlify/images/grid-white.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/mintlify/images/grid.svg b/mintlify/images/grid.svg deleted file mode 100644 index 002e85b5..00000000 --- a/mintlify/images/grid.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/mintlify/images/linkedin.svg b/mintlify/images/linkedin.svg deleted file mode 100644 index 1dc2a4ef..00000000 --- a/mintlify/images/linkedin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/mintlify/images/postman-white.svg b/mintlify/images/postman-white.svg deleted file mode 100644 index e51dc507..00000000 --- a/mintlify/images/postman-white.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mintlify/images/postman.svg b/mintlify/images/postman.svg deleted file mode 100644 index 81a4b657..00000000 --- a/mintlify/images/postman.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mintlify/images/x-twitter.svg b/mintlify/images/x-twitter.svg deleted file mode 100644 index d76a8959..00000000 --- a/mintlify/images/x-twitter.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/mintlify/index.mdx b/mintlify/index.mdx deleted file mode 100644 index d0475056..00000000 --- a/mintlify/index.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -title: "Lightspark Grid" -mode: "custom" ---- - -export const GridLogo = () => { - return ( - <> - Lightspark Grid - Lightspark Grid - - ); -}; - -export const HeroCard = ({ title, description, href, icon, whiteIcon }) => { - return ( - - Icon - Icon -

- - ); -}; - -
-
-
-
- - - Grid - -
-
-

- Commands for money. One API to send, - receive, and settle value globally. Fiat, stablecoins, or BTC. Always - real time, always low-cost, built on Bitcoin. -

-
-
- -
-
-
-
-
-
-

Build with Lightspark Grid

-

Choose your use case

-
-
-
- - - - -
-
-
-
-
-
-
-

Resources

-

Explore, test, and integrate

-
-
-
- - - -
-
-
-
- - diff --git a/mintlify/logo/dark.svg b/mintlify/logo/dark.svg deleted file mode 100644 index 480f9f12..00000000 --- a/mintlify/logo/dark.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/mintlify/logo/light.svg b/mintlify/logo/light.svg deleted file mode 100644 index 471739a0..00000000 --- a/mintlify/logo/light.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml deleted file mode 100644 index 90d2e8af..00000000 --- a/mintlify/openapi.yaml +++ /dev/null @@ -1,6897 +0,0 @@ -openapi: 3.1.0 -info: - title: Grid API - description: | - API for managing global payments on the open Money Grid. Built by Lightspark. See the full documentation at https://grid.lightspark.com/. - version: '2025-10-13' - contact: - name: Lightspark Support - email: support@lightspark.com - license: - name: Proprietary - url: https://lightspark.com/terms -servers: - - url: https://api.lightspark.com/grid/2025-10-13 - description: Production server -security: - - BasicAuth: [] -tags: - - name: Platform Configuration - description: Platform configuration endpoints for managing global settings. You can also configure these settings in the Grid dashboard. - - name: Customers - description: Customer management endpoints for creating and updating customer information - - name: Internal Accounts - description: Internal account management endpoints for creating and managing internal accounts - - name: External Accounts - description: External account management endpoints for creating and managing external bank accounts - - name: Same-Currency Transfers - description: Endpoints for transferring funds between internal and external accounts with the same currency - - name: Cross-Currency Transfers - description: Endpoints for creating and confirming quotes for cross-currency transfers - - name: Transactions - description: Endpoints for retrieving transaction information - - name: Webhooks - description: Webhook endpoints and configuration for receiving notifications - - name: Invitations - description: Endpoints for creating, claiming and managing UMA invitations - - name: Sandbox - description: Endpoints to trigger test cases in sandbox - - name: API Tokens - description: Endpoints to programmatically manage API tokens -paths: - /config: - get: - summary: Get platform configuration - description: Retrieve the current platform configuration - operationId: getPlatformConfig - tags: - - Platform Configuration - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/PlatformConfig' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - patch: - summary: Update platform configuration - description: Update the platform configuration settings - operationId: updatePlatformConfig - tags: - - Platform Configuration - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - umaDomain: - type: string - example: mycompany.com - webhookEndpoint: - type: string - example: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - type: array - items: - $ref: '#/components/schemas/PlatformCurrencyConfig' - example: - umaDomain: mycompany.com - webhookEndpoint: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - - currencyCode: USD - minAmount: 100 - maxAmount: 1000000 - enabledTransactionTypes: - - OUTGOING - - INCOMING - requiredCounterpartyFields: - - name: FULL_NAME - mandatory: true - - name: NATIONALITY - mandatory: true - - name: BIRTH_DATE - mandatory: true - responses: - '200': - description: Configuration updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/PlatformConfig' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - '501': - description: Not implemented - content: - application/json: - schema: - $ref: '#/components/schemas/Error501' - /customers: - post: - summary: Add a new customer - description: Register a new customer in the system with an account identifier and bank account information - operationId: createCustomer - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CustomerCreateRequestOneOf' - responses: - '201': - description: Customer created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/CustomerOneOf' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Customer with the UMA address already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - '501': - description: Not implemented - content: - application/json: - schema: - $ref: '#/components/schemas/Error501' - get: - summary: List customers - description: | - Retrieve a list of customers with optional filtering parameters. Returns all customers that match - the specified filters. If no filters are provided, returns all customers (paginated). - operationId: listCustomers - tags: - - Customers - security: - - BasicAuth: [] - parameters: - - name: platformCustomerId - in: query - description: Filter by platform-specific customer identifier - required: false - schema: - type: string - - name: customerType - in: query - description: Filter by customer type - required: false - schema: - $ref: '#/components/schemas/CustomerType' - - name: createdAfter - in: query - description: Filter customers created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter customers created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedAfter - in: query - description: Filter customers updated after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedBefore - in: query - description: Filter customers updated before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: umaAddress - in: query - description: Filter by uma address - required: false - schema: - type: string - - name: isIncludingDeleted - in: query - description: Whether to include deleted customers in the results. Default is false. - required: false - schema: - type: boolean - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of customers matching the filter criteria - items: - $ref: '#/components/schemas/CustomerOneOf' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of customers matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/{customerId}: - parameters: - - name: customerId - in: path - description: System-generated unique customer identifier - required: true - schema: - type: string - get: - summary: Get customer by ID - description: Retrieve a customer by their system-generated ID - operationId: getCustomerById - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - patch: - summary: Update customer by ID - description: Update a customer's metadata by their system-generated ID - operationId: updateCustomerById - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CustomerUpdateRequestOneOf' - examples: - individualUpdate: - summary: Update individual customer example - value: - customerType: INDIVIDUAL - fullName: John Smith - birthDate: '1985-06-15' - address: - line1: 456 Market St - city: San Francisco - state: CA - postalCode: '94103' - country: US - businessUpdate: - summary: Update business customer example - value: - customerType: BUSINESS - businessInfo: - legalName: New Tech Solutions LLC - registrationNumber: BRN-987654321 - taxId: EIN-123456789 - address: - line1: 100 Technology Parkway - city: Palo Alto - state: CA - postalCode: '94304' - country: US - responses: - '200': - description: Customer updated successfully - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - delete: - summary: Delete customer by ID - description: Delete a customer by their system-generated ID - operationId: deleteCustomerById - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Customer deleted successfully - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '410': - description: Customer deleted already - content: - application/json: - schema: - $ref: '#/components/schemas/Error410' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/kyc-link: - parameters: - - name: redirectUri - in: query - description: An optional uri a customer will be redirected to after completing the hosted KYC flow - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: The platform id of the customer to onboard - required: true - schema: - type: string - get: - summary: Get a KYC link for onboarding a customer - description: Generate a hosted KYC link to onboard a customer - operationId: getKycLinkForCustomer - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - properties: - kycUrl: - type: string - description: A hosted KYC link for your customer to complete KYC - example: https://example.com/redirect - platformCustomerId: - type: string - description: The platform id of the customer to onboard - example: 019542f5-b3e7-1d02-0000-000000000001 - customerId: - type: string - description: The customer id of the newly created customer on the system - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/internal-accounts: - get: - summary: List Customer internal accounts - description: | - Retrieve a list of internal accounts with optional filtering parameters. Returns all - internal accounts that match the specified filters. If no filters are provided, returns all internal accounts - (paginated). - - Internal accounts are created automatically when a customer is created based on the platform configuration. - operationId: listCustomerInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by internal accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: '#/components/schemas/InternalAccount' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of customers matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /platform/internal-accounts: - get: - summary: List platform internal accounts - description: | - Retrieve a list of all internal accounts that belong to the platform, as opposed to an individual customer. - - These accounts are created automatically when the platform is configured for each supported currency. They can be used for things like distributing bitcoin rewards to customers, or for other platform-wide purposes. - operationId: listPlatformInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: '#/components/schemas/InternalAccount' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/external-accounts: - get: - summary: List Customer external accounts - description: | - Retrieve a list of external accounts with optional filtering parameters. Returns all - external accounts that match the specified filters. If no filters are provided, returns all external accounts - (paginated). - - External accounts are bank accounts, cryptocurrency wallets, or other payment destinations that customers can use to receive funds from the platform. - operationId: listCustomerExternalAccounts - tags: - - External Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by external accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: '#/components/schemas/ExternalAccount' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of external accounts matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - post: - summary: Add a new external account - description: |- - Register a new external bank account for a customer. - - **Sandbox Testing:** In sandbox mode, use these account number patterns to test different transfer scenarios. These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createCustomerExternalAccount - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccountCreateRequest' - examples: - usBankAccount: - summary: Create external US bank account - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: USD - accountInfo: - accountType: US_ACCOUNT - accountNumber: '12345678901' - routingNumber: '123456789' - accountCategory: CHECKING - bankName: Chase Bank - platformAccountId: ext_acc_123456 - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: '1990-01-15' - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: '94105' - country: US - sparkWallet: - summary: Create external Spark wallet - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - responses: - '201': - description: External account created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - External account already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /platform/external-accounts: - get: - summary: List platform external accounts - description: | - Retrieve a list of all external accounts that belong to the platform, as opposed to an individual customer. - - These accounts are used for platform-wide operations such as receiving funds from external sources or managing platform-level payment destinations. - operationId: listPlatformExternalAccounts - tags: - - External Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - post: - summary: Add a new platform external account - description: |- - Register a new external bank account for the platform. - - **Sandbox Testing:** In sandbox mode, use these account number patterns to test different transfer scenarios. These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createPlatformExternalAccount - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccountCreateRequest' - examples: - usBankAccount: - summary: Create external US bank account - value: - currency: USD - accountInfo: - accountType: US_ACCOUNT - accountNumber: '12345678901' - routingNumber: '123456789' - accountCategory: CHECKING - bankName: Chase Bank - platformAccountId: ext_acc_123456 - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: '1990-01-15' - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: '94105' - country: US - sparkWallet: - summary: Create external Spark wallet - value: - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - responses: - '201': - description: External account created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - External account already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /plaid/link-tokens: - post: - summary: Request Plaid Link token - description: | - Creates a Plaid Link token that can be used to initialize Plaid Link in your application. - The Link token is used to authenticate the customer and allow them to select their bank account. - - **Async Flow:** - 1. Platform calls this endpoint to get a link_token and callbackUrl - 2. Platform displays Plaid Link UI to the end customer using the link_token - 3. End customer authenticates with their bank and selects an account - 4. Plaid returns a public_token to the platform - 5. Platform POSTs the public_token to the callbackUrl - 6. Lightspark exchanges the public_token with Plaid and creates the external account asynchronously - 7. Platform receives a webhook notification when the external account is ready - operationId: createPlaidLinkToken - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PlaidLinkTokenRequest' - example: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - responses: - '200': - description: Link token created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/PlaidLinkTokenResponse' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /plaid/callback/{plaid_link_token}: - post: - summary: Submit Plaid public token - description: | - After the customer completes Plaid Link authentication, the platform should POST - the public_token to this callback URL (provided in the link token response). - - This will trigger asynchronous processing: - 1. Lightspark exchanges the public_token for an access_token with Plaid - 2. Lightspark retrieves and verifies the account details - 3. An external account is created - 4. A webhook notification is sent to the platform when complete - operationId: submitPlaidPublicToken - tags: - - External Accounts - parameters: - - name: plaid_link_token - in: path - required: true - description: The Plaid link token from the link token response, used to identify the session - schema: - type: string - example: link-sandbox-abc123xyz-1234-5678 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PlaidCallbackRequest' - example: - publicToken: public-sandbox-12345678-1234-1234-1234-123456789012 - accountId: plaid_account_id_123 - responses: - '202': - description: | - A pending external account resource will be created and returned while the Grid API asynchronously processes the Plaid public token. - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - Invalid public token or link token - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '404': - description: Link token not found or expired - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Link token already used - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transfer-in: - post: - summary: Create a transfer-in request - description: | - Transfer funds from an external account to an internal account for a specific customer. This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). Otherwise, use the paymentInstructions on the internal account to deposit funds. - operationId: createTransferIn - tags: - - Same-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - source - - destination - properties: - source: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an external account ID - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Source external account details - destination: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an internal account ID - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination internal account details - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - examples: - transferIn: - summary: Transfer from external to internal account - value: - source: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - amount: 12550 - responses: - '201': - description: Transfer-in request created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Transaction' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer or account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transfer-out: - post: - summary: Create a transfer-out request - description: | - Transfer funds from an internal account to an external account for a specific customer. - operationId: createTransferOut - tags: - - Same-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - source - - destination - properties: - source: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an internal account ID - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Source internal account details - destination: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an external account ID - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Destination external account details - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - examples: - transferOut: - summary: Transfer from internal to external account - value: - source: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - destination: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - amount: 12550 - responses: - '201': - description: Transfer-out request created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Transaction' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer or account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /receiver/uma/{receiverUmaAddress}: - get: - summary: Look up an UMA address for payment - description: | - Lookup a receiving UMA address to determine supported currencies and exchange rates. - This endpoint helps platforms determine what currencies they can send to a given UMA address. - operationId: lookupUma - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: receiverUmaAddress - in: path - description: UMA address of the intended recipient - required: true - schema: - type: string - - name: senderUmaAddress - in: query - description: UMA address of the sender (optional if customerId is provided) - required: false - schema: - type: string - - name: customerId - in: query - description: System ID of the sender (optional if senderUmaAddress is provided) - required: false - schema: - type: string - responses: - '200': - description: Successful lookup - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ReceiverLookupResponse' - - type: object - required: - - receiverUmaAddress - properties: - receiverUmaAddress: - type: string - description: The UMA address that was looked up - example: $receiver@uma.domain - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: UMA address not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /receiver/external-account/{accountId}: - get: - summary: Look up an external account for payment - description: | - Lookup an external account by ID to determine supported currencies and exchange rates. - This endpoint helps platforms determine what currencies they can send to a given external account, along with the current estimated exchange rates and minimum and maximum amounts that can be sent. - operationId: lookupExternalAccount - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: accountId - in: path - description: System-generated ID of the external account - required: true - schema: - type: string - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - - name: senderUmaAddress - in: query - description: UMA address of the sender (optional if customerId is provided) - required: false - schema: - type: string - - name: customerId - in: query - description: System ID of the sender (optional if senderUmaAddress is provided) - required: false - schema: - type: string - responses: - '200': - description: Successful lookup - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ReceiverLookupResponse' - - type: object - required: - - accountId - properties: - accountId: - type: string - description: The external account ID that was looked up - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: External account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /quotes/{quoteId}: - get: - summary: Get quote by ID - description: | - Retrieve a quote by its ID. If the quote has been settled, it will include - the transaction ID. This allows clients to track the full lifecycle of a payment - from quote creation to settlement. - operationId: getQuoteById - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: quoteId - in: path - description: ID of the quote to retrieve - required: true - schema: - type: string - responses: - '200': - description: Quote retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /quotes: - post: - summary: Create a transfer quote - description: | - Generate a quote for a cross-currency transfer between any combination of accounts - and UMA addresses. This endpoint handles currency exchange and provides the necessary - instructions to execute the transfer. - - **Transfer Types Supported:** - - **Account to Account**: Transfer between internal/external accounts with currency exchange. - - **Account to UMA**: Transfer from an internal account to an UMA address. - - **UMA to Account or UMA to UMA**: This transfer type will only be funded by payment instructions, not from an internal account. - - **Key Features:** - - **Flexible Amount Locking**: Always specify whether you want to lock the sending amount or receiving amount - - **Currency Exchange**: Handles all cross-currency transfers with real-time exchange rates - - **Payment Instructions**: For UMA or customer ID sources, provides banking details needed for execution - - **Important:** If you are transferring funds in the same currency (no exchange required), - use the `/transfer-in` or `/transfer-out` endpoints instead. - - **Sandbox Testing:** When using the `externalAccountDetails` destination type in sandbox mode, use account number patterns ending in specific digits to test different scenarios. - These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. - For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createQuote - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/QuoteRequest' - examples: - accountToAccount: - summary: Account to Account Transfer - value: - source: - accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - currency: EUR - lockedCurrencySide: SENDING - lockedCurrencyAmount: 10000 - description: Transfer between accounts, either internal or external. - accountToUma: - summary: Account to UMA Address Transfer - value: - lookupId: LookupRequest:019542f5-b3e7-1d02-0000-000000000009 - source: - accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - umaAddress: $receiver@uma.domain.com - currency: EUR - lockedCurrencySide: SENDING - lockedCurrencyAmount: 1000 - description: 'Payment for invoice #1234' - realTimeFundingToSparkWallet: - summary: Real-time funding to Spark Wallet as an on-ramp flow. Immediate execution. - value: - source: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000009 - currency: USD - destination: - externalAccountDetails: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - lockedCurrencySide: RECEIVING - lockedCurrencyAmount: 10000 - immediatelyExecute: true - description: Bitcoin reward payout! - responses: - '201': - description: | - Transfer quote created successfully. The response includes exchange rates, - fees, and transfer details. For transfers involving UMA addresses, payment - instructions are also included for execution through banking systems. - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - '501': - description: Not implemented - content: - application/json: - schema: - $ref: '#/components/schemas/Error501' - get: - summary: List transfer quotes - description: | - Retrieve a list of transfer quotes with optional filtering parameters. Returns all - quotes that match the specified filters. If no filters are provided, returns all quotes - (paginated). - operationId: listQuotes - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: customerId - in: query - description: Filter by sending customer ID - required: false - schema: - type: string - - name: sendingAccountId - in: query - description: Filter by sending account ID - required: false - schema: - type: string - - name: receivingAccountId - in: query - description: Filter by receiving account ID - required: false - schema: - type: string - - name: sendingUmaAddress - in: query - description: Filter by sending UMA address - required: false - schema: - type: string - - name: receivingUmaAddress - in: query - description: Filter by receiving UMA address - required: false - schema: - type: string - - name: status - in: query - description: Filter by quote status - required: false - schema: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - - name: createdAfter - in: query - description: Filter quotes created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter quotes created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of quotes matching the criteria - items: - $ref: '#/components/schemas/Quote' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of quotes matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /quotes/{quoteId}/execute: - post: - summary: Execute a quote - description: | - Execute a quote by its ID. This endpoint initiates the transfer between - the source and destination accounts. - - This endpoint can only be used for quotes with a `source` which is either an internal account, - or has direct pull functionality (e.g. ACH pull with an external account). - - Once executed, the quote cannot be cancelled and the transfer will be processed. - operationId: executeQuote - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: quoteId - in: path - required: true - description: The unique identifier of the quote to execute - schema: - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - responses: - '200': - description: | - Quote confirmed successfully. The transfer has been initiated and - the quote status has been updated. - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '400': - description: Bad request - Invalid quote ID or quote cannot be confirmed - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Quote already confirmed, expired, or in invalid state - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions: - get: - summary: List transactions - description: | - Retrieve a paginated list of transactions with optional filtering. - The transactions can be filtered by customer ID, platform customer ID, UMA address, - date range, status, and transaction type. - operationId: listTransactions - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: customerId - in: query - description: Filter by system customer ID - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: Filter by platform-specific customer ID - required: false - schema: - type: string - - name: senderAccountIdentifier - in: query - description: Filter by sender account identifier - required: false - schema: - type: string - - name: receiverAccountIdentifier - in: query - description: Filter by receiver account identifier - required: false - schema: - type: string - - name: status - in: query - description: Filter by transaction status - required: false - schema: - $ref: '#/components/schemas/TransactionStatus' - - name: type - in: query - description: Filter by transaction type - required: false - schema: - $ref: '#/components/schemas/TransactionType' - - name: reference - in: query - description: Filter by reference - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of transactions matching the criteria - items: - $ref: '#/components/schemas/Transaction' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of transactions matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions/{transactionId}: - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction - required: true - schema: - type: string - get: - summary: Get transaction by ID - description: Retrieve detailed information about a specific transaction. - operationId: getTransactionById - tags: - - Transactions - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/Transaction' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions/{transactionId}/approve: - post: - summary: Approve a pending incoming payment - description: | - Approve a pending incoming payment that was previously acknowledged with a 202 response. - This endpoint allows platforms to asynchronously approve payments after async processing. - operationId: approvePendingPayment - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction to approve - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - receiverCustomerInfo: - type: object - additionalProperties: true - description: Information about the recipient, provided by the platform if requested in the original webhook via `requestedReceiverCustomerInfoFields`. - responses: - '200': - description: Payment approved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingTransaction' - '400': - description: Bad request - Invalid parameters or payment cannot be approved - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Payment is not in a pending state or has already been processed or timed out. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions/{transactionId}/reject: - post: - summary: Reject a pending incoming payment - description: | - Reject a pending incoming payment that was previously acknowledged with a 202 response. - This endpoint allows platforms to asynchronously reject payments after additional processing. - operationId: rejectPendingPayment - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction to reject - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - reason: - type: string - description: Optional reason for rejecting the payment. This is just for debugging purposes or can be used for a platform's own purposes. - example: RESTRICTED_JURISDICTION - responses: - '200': - description: Payment rejected successfully - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingTransaction' - '400': - description: Bad request - Invalid parameters or payment cannot be rejected - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Payment is not in a pending state or has already been processed or timed out. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /webhooks/test: - post: - summary: Send a test webhook - description: Send a test webhook to the configured endpoint - operationId: sendTestWebhook - tags: - - Webhooks - security: - - BasicAuth: [] - responses: - '200': - description: Webhook delivered successfully - content: - application/json: - schema: - $ref: '#/components/schemas/TestWebhookResponse' - '400': - description: Bad request - Webhook delivery failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/bulk/csv: - post: - summary: Upload customers via CSV file - description: | - Upload a CSV file containing customer information for bulk creation. The CSV file should follow - a specific format with required and optional columns based on customer type. - - ### CSV Format - The CSV file should have the following columns: - - Required columns for all customers: - - umaAddress: The customer's UMA address (e.g., $john.doe@uma.domain.com) - - platformCustomerId: Your platform's unique identifier for the customer - - customerType: Either "INDIVIDUAL" or "BUSINESS" - - Required columns for individual customers: - - fullName: Individual's full name - - birthDate: Date of birth in YYYY-MM-DD format - - addressLine1: Street address line 1 - - city: City - - state: State/Province/Region - - postalCode: Postal/ZIP code - - country: Country code (ISO 3166-1 alpha-2) - - Required columns for business customers: - - businessLegalName: Legal name of the business - - addressLine1: Street address line 1 - - city: City - - state: State/Province/Region - - postalCode: Postal/ZIP code - - country: Country code (ISO 3166-1 alpha-2) - - Optional columns for all customers: - - addressLine2: Street address line 2 - - platformAccountId: Your platform's identifier for the bank account - - description: Optional description for the customer - - Optional columns for individual customers: - - email: Customer's email address - - Optional columns for business customers: - - businessRegistrationNumber: Business registration number - - businessTaxId: Tax identification number - - ### Example CSV - ```csv - umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName - john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US - acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US - ``` - - The upload process is asynchronous and will return a job ID that can be used to track progress. - You can monitor the job status using the `/customers/bulk/jobs/{jobId}` endpoint. - operationId: uploadCustomersCsv - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - file - properties: - file: - type: string - format: binary - description: CSV file containing customer information - responses: - '202': - description: CSV upload accepted for processing - content: - application/json: - schema: - type: object - required: - - jobId - - status - properties: - jobId: - type: string - description: Unique identifier for the bulk import job - example: Job:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/bulk/jobs/{jobId}: - get: - summary: Get bulk import job status - description: | - Retrieve the current status and results of a bulk customer import job. This endpoint can be used - to track the progress of both CSV uploads. - - The response includes: - - Overall job status - - Progress statistics - - Detailed error information for failed entries - - Completion timestamp when finished - operationId: getBulkCustomerImportJob - tags: - - Customers - security: - - BasicAuth: [] - parameters: - - name: jobId - in: path - description: ID of the bulk import job to retrieve - required: true - schema: - type: string - responses: - '200': - description: Job status retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/BulkCustomerImportJob' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Job not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations: - post: - summary: Create an UMA invitation - description: | - Create an UMA invitation from a given platform customer. - operationId: createInvitation - tags: - - Invitations - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - inviterUma - properties: - inviterUma: - type: string - description: The UMA address of the customer creating the invitation - example: $inviter@uma.domain - firstName: - type: string - description: First name of the invitee to show as part of the invite - example: Alice - amountToSend: - description: | - An amount to send (in the smallest unit of the customer's currency) to the invitee when the invitation is claimed. - This is optional and if not provided, the invitee will not receive any amount. Note that the actual sending of - the amount must be done by the inviter platform once the INVITATION_CLAIMED webhook is received. If the inviter - platform either does not send the payment or the payment fails, the invitee will not receive this amount. This - field is primarily used for display purposes on the claiming side of the invitation. - type: integer - format: int64 - example: 12550 - expiresAt: - type: string - format: date-time - description: When the invitation expires (if at all) - example: '2025-09-01T14:30:00Z' - responses: - '201': - description: Invitation created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations/{invitationCode}: - get: - summary: Get an UMA invitation by code - description: | - Retrieve details about an UMA invitation by its invitation code. - operationId: getInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to get - required: true - schema: - type: string - responses: - '200': - description: Invitation retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations/{invitationCode}/claim: - post: - summary: Claim an UMA invitation - description: | - Claim an UMA invitation by associating it with an invitee UMA address. - - When an invitation is successfully claimed: - 1. The invitation status changes from PENDING to CLAIMED - 2. The invitee UMA address is associated with the invitation - 3. An INVITATION_CLAIMED webhook is triggered to notify the platform that created the invitation - - This endpoint allows customers to accept invitations sent to them by other UMA customers. - operationId: claimInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to claim - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - inviteeUma - properties: - inviteeUma: - type: string - description: The UMA address of the customer claiming the invitation - example: $invitee@uma.domain - responses: - '200': - description: Invitation claimed successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations/{invitationCode}/cancel: - post: - summary: Cancel an UMA invitation - description: | - Cancel a pending UMA invitation. Only the inviter or platform can cancel an invitation. - - When an invitation is cancelled: - 1. The invitation status changes from PENDING to CANCELLED - 2. The invitation can no longer be claimed - 3. The invitation URL will show as cancelled when accessed - - Only pending invitations can be cancelled. Attempting to cancel an invitation - that is already claimed, expired, or cancelled will result in an error. - operationId: cancelInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to cancel - required: true - schema: - type: string - responses: - '200': - description: Invitation cancelled successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '400': - description: Bad request - Invitation cannot be cancelled (already claimed, expired, or cancelled) - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Only the platform which created the invitation can cancel it - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /sandbox/send: - post: - summary: Simulate sending funds - description: | - Simulate sending funds to the bank account as instructed in the quote. - This endpoint is only for the sandbox environment and will fail for production platforms/keys. - operationId: sandboxSend - tags: - - Sandbox - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - quoteId - - currencyCode - properties: - quoteId: - type: string - description: The unique identifier of the quote - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - currencyCode: - type: string - description: Currency code for the funds to be sent - example: USD - currencyAmount: - type: integer - format: int64 - description: The amount to send in the smallest unit of the currency (eg. cents). If not provided, the amount will be derived from the quote. - exclusiveMinimum: 0 - example: 1000 - responses: - '200': - description: Funds received successfully - content: - application/json: - schema: - $ref: '#/components/schemas/OutgoingTransaction' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /sandbox/uma/receive: - post: - summary: Simulate payment send to test receiving an UMA payment - description: | - Simulate sending payment from an sandbox uma address to a platform customer to test payment receive. - This endpoint is only for the sandbox environment and will fail for production platforms/keys. - operationId: sandboxReceive - tags: - - Sandbox - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - senderUmaAddress - - receivingCurrencyCode - - receivingCurrencyAmount - properties: - senderUmaAddress: - type: string - description: UMA address of the sender from the sandbox - example: $success.usd@sandbox.grid.uma.money - receiverUmaAddress: - type: string - description: UMA address of the receiver (optional if customerId is provided) - example: $receiver@uma.domain - customerId: - type: string - description: System ID of the receiver (optional if receiverUmaAddress is provided) - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - receivingCurrencyCode: - type: string - description: The currency code for the receiving amount - example: USD - receivingCurrencyAmount: - type: integer - format: int64 - description: The amount to be received in the smallest unit of the currency (eg. cents) - exclusiveMinimum: 0 - example: 1000 - responses: - '200': - description: Payment triggered successfully - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingTransaction' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Sender or receiver not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /sandbox/internal-accounts/{accountId}/fund: - post: - summary: Simulate funding an internal account - description: | - Simulate receiving funds into an internal account in the sandbox environment. This is useful for testing scenarios where you need to add funds to a customer's or platform's internal account without going through a real bank transfer or following payment instructions. - This endpoint is only for the sandbox environment and will fail for production platforms/keys. - operationId: sandboxFundInternalAccount - tags: - - Sandbox - security: - - BasicAuth: [] - parameters: - - name: accountId - in: path - required: true - description: The ID of the internal account to fund - schema: - type: string - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - amount - properties: - amount: - type: integer - format: int64 - description: Amount to add in the smallest unit of the account's currency (e.g., cents for USD/EUR, satoshis for BTC) - exclusiveMinimum: 0 - maximum: 100000000000 - example: 100000 - examples: - fundUSDAccount: - summary: Fund USD internal account with $1,000 - value: - amount: 100000 - fundBTCAccount: - summary: Fund BTC internal account with 0.01 BTC - value: - amount: 1000000 - responses: - '200': - description: Internal account funded successfully - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccount' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Internal account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /uma-providers: - get: - summary: List available Counterparty Providers - description: | - Retrieve a list of available Counterparty Providers. The response includes basic information about each provider, such as its UMA address, name, and supported currencies. - operationId: getAvailableUmaProviders - tags: - - Available UMA Providers - parameters: - - in: query - name: countryCode - description: The alpha-2 representation of a country, as defined by the ISO 3166-1 standard. - required: false - schema: - type: string - example: US - - in: query - name: currencyCode - description: The ISO 4217 currency code to filter providers by supported currency. - required: false - schema: - type: string - example: USD - - in: query - name: hasBlockedProviders - description: Whether to include providers which are not on your allowlist in the response. By default the response will include blocked providers. - required: false - schema: - type: boolean - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - properties: - data: - description: List of available UMA Providers using Grid - type: array - items: - $ref: '#/components/schemas/UmaProvider' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of transactions matching the criteria (excluding pagination) - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /tokens: - post: - summary: Create a new API token - description: Create a new API token to access the Grid APIs. - operationId: createToken - tags: - - API Tokens - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - title: tokenCreate - properties: - name: - type: string - description: Name of the token to help identify it - example: Sandbox read-only - permissions: - type: array - description: A list of permissions to grant to the token - items: - $ref: '#/components/schemas/Permission' - required: - - name - - permissions - responses: - '201': - description: API token created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ApiToken' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - get: - summary: List tokens - description: | - Retrieve a list of API tokens with optional filtering parameters. Returns all tokens that match - the specified filters. If no filters are provided, returns all tokens (paginated). - operationId: listTokens - tags: - - API Tokens - security: - - BasicAuth: [] - parameters: - - name: name - in: query - description: Filter by name of the token - required: false - schema: - type: string - - name: createdAfter - in: query - description: Filter customers created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter customers created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedAfter - in: query - description: Filter customers updated after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedBefore - in: query - description: Filter customers updated before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of tokens matching the filter criteria - items: - $ref: '#/components/schemas/ApiToken' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of tokens matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /tokens/{tokenId}: - parameters: - - name: tokenId - in: path - description: System-generated unique token identifier - required: true - schema: - type: string - get: - summary: Get API token by ID - description: Retrieve an API token by their system-generated ID - operationId: getTokenById - tags: - - API Tokens - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiToken' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Token not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - delete: - summary: Delete API token by ID - description: Delete an API token by their system-generated ID - operationId: deleteTokenById - tags: - - API Tokens - security: - - BasicAuth: [] - responses: - '204': - description: API token deleted successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Token not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' -webhooks: - incoming-payment: - post: - summary: Incoming payment webhook and approval mechanism - description: | - Webhook that is called when an incoming payment is received by a customer's UMA address. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - ### Payment Approval Flow - When a transaction has `status: "PENDING"`, this webhook serves as an approval mechanism: - - 1. The client should check the `counterpartyInformation` against their requirements - 2. To APPROVE the payment synchronously, return a 200 OK response - 3. To REJECT the payment, return a 403 Forbidden response with an Error object - 4. To request more information, return a 422 Unprocessable Entity with specific missing fields - 5. To process the payment asynchronously, return a 202 Accepted response and then call the `/transactions/{transactionId}/approve` or `/transactions/{transactionId}/reject` endpoint within 5 seconds. Note that synchronous approval/rejection is preferred where possible. - - The Grid system will proceed or cancel the payment based on your response. - - For transactions with other statuses (COMPLETED, FAILED, REFUNDED), this webhook is purely informational. - operationId: incomingPaymentWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhook' - examples: - pendingPayment: - summary: Pending payment example requiring approval - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: PENDING - type: INCOMING - senderUmaAddress: $sender@external.domain - receiverUmaAddress: $recipient@uma.domain - receivedAmount: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - reconciliationInstructions: - reference: REF-123456789 - counterpartyInformation: - FULL_NAME: John Sender - BIRTH_DATE: '1985-06-15' - NATIONALITY: US - requestedReceiverCustomerInfoFields: - - name: NATIONALITY - mandatory: true - - name: ADDRESS - mandatory: false - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: INCOMING_PAYMENT - incomingCompletedPayment: - summary: Completed payment notification - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: COMPLETED - type: INCOMING - senderUmaAddress: $sender@external.domain - receiverUmaAddress: $recipient@uma.domain - receivedAmount: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - settledAt: '2025-08-15T14:30:00Z' - createdAt: '2025-08-15T14:25:18Z' - description: Payment for services - reconciliationInstructions: - reference: REF-123456789 - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: INCOMING_PAYMENT - responses: - '200': - description: | - Webhook received successfully. - For PENDING transactions, this indicates approval to proceed with the payment. - If `requestedReceiverCustomerInfoFields` were present in the webhook request, the corresponding fields for the recipient must be included in this response in the `receiverCustomerInfo` object. - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhookResponse' - '202': - description: | - Webhook received and will be processed asynchronously. The synchronous 200 response should be preferred where possible. This asycnhronous path should only be used in - cases where the platform's architecture requires async (but still very quick) processing before approving or rejecting the payment. - The platform must call the `/transactions/{transactionId}/approve` or `/transactions/{transactionId}/reject` endpoint to approve or reject the payment within 5 seconds or the payment will be automatically rejected. - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: | - Forbidden - Payment rejected by the client. - Only applicable for PENDING transactions. - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhookForbiddenResponse' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '422': - description: | - Unprocessable Entity - Additional counterparty information required. - Only applicable for PENDING transactions. - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhookUnprocessableResponse' - outgoing-payment: - post: - summary: Outgoing payment status webhook - description: | - Webhook that is called when an outgoing payment's status changes. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - This webhook is informational only and is sent when an outgoing payment completes successfully or fails. - operationId: outgoingPaymentWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/OutgoingPaymentWebhook' - examples: - outgoingCompletedPayment: - summary: Completed outgoing payment - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: COMPLETED - type: OUTGOING - senderUmaAddress: $sender@uma.domain - receiverUmaAddress: $recipient@external.domain - sentAmount: - amount: 10550 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - receivedAmount: - amount: 9706 - currency: - code: EUR - name: Euro - symbol: € - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - settlementTime: '2025-08-15T14:30:00Z' - createdAt: '2025-08-15T14:25:18Z' - description: 'Payment for invoice #1234' - exchangeRate: 0.92 - quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006 - paymentInstructions: - - accountOrWalletInfo: - reference: UMA-Q12345-REF - accountType: US_ACCOUNT - accountNumber: 987654321 - routingNumber: 123456789 - accountCategory: CHECKING - bankName: Chase Bank - - accountOrWalletInfo: - accountType: SOLANA_WALLET - assetType: USDC - address: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: OUTGOING_PAYMENT - failedPayment: - summary: Failed outgoing payment - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: FAILED - type: OUTGOING - senderUmaAddress: $sender@uma.domain - receiverUmaAddress: $recipient@external.domain - sentAmount: - amount: 10550 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - createdAt: '2025-08-15T14:25:18Z' - quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006 - paymentInstructions: - - accountOrWalletInfo: - reference: UMA-Q12345-REF - accountType: US_ACCOUNT - accountNumber: 987654321 - routingNumber: 123456789 - accountCategory: CHECKING - bankName: Chase Bank - - accountOrWalletInfo: - accountType: SOLANA_WALLET - assetType: USDC - address: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: OUTGOING_PAYMENT - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - test-webhook: - post: - summary: Test webhook for integration verification - description: | - Webhook that is sent once to verify your webhook endpoint is correctly set up. - This is sent when you configure or update your platform settings with a webhook URL. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by the Grid API. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - This webhook is purely for testing your endpoint integration and signature verification. - operationId: testWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TestWebhookRequest' - examples: - testWebhook: - summary: Test webhook example - value: - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000001 - type: TEST - responses: - '200': - description: Webhook received successfully. This confirms your webhook endpoint is properly configured. - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - bulk-upload: - post: - summary: Bulk upload status webhook - description: | - Webhook that is called when a bulk customer upload job completes or fails. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - This webhook is sent when a bulk upload job completes or fails, providing detailed information about the results. - operationId: bulkUploadWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BulkUploadWebhookRequest' - examples: - completedUpload: - summary: Successful bulk upload completion - value: - bulkCustomerImportJob: - jobId: Job:019542f5-b3e7-1d02-0000-000000000006 - status: COMPLETED - progress: - total: 5000 - processed: 5000 - successful: 5000 - failed: 0 - errors: [] - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: BULK_UPLOAD - timestamp: '2025-08-15T14:32:00Z' - failedUpload: - summary: Failed bulk upload - value: - bulkCustomerImportJob: - jobId: Job:019542f5-b3e7-1d02-0000-000000000006 - status: FAILED - progress: - total: 5000 - processed: 5000 - successful: 0 - failed: 5000 - errors: - - correlationId: row_1 - error: - code: invalid_csv_format - message: Invalid CSV format - details: - reason: missing_required_column - column: umaAddress - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: BULK_UPLOAD - timestamp: '2025-08-15T14:32:00Z' - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - invitation-claimed: - post: - summary: Invitation claimed webhook - description: | - Webhook that is called when an invitation is claimed by a customer. - This endpoint should be implemented by platform clients of the Grid API. - - When a customer claims an invitation, this webhook is triggered to notify the platform that: - 1. The invitation has been successfully claimed - 2. The invitee UMA address is now associated with the invitation - 3. The invitation status has changed from PENDING to CLAIMED - - This allows platforms to: - - Track invitation usage and conversion rates - - Trigger onboarding flows for new customers who joined via invitation - - Apply referral bonuses or rewards to the inviter - - Update their UI to reflect the claimed status - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - operationId: invitationClaimedWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/InvitationClaimedWebhook' - examples: - claimedInvitation: - summary: Invitation claimed notification - value: - invitation: - code: 019542f5 - createdAt: '2025-09-01T14:30:00Z' - claimedAt: '2025-09-01T15:45:00Z' - inviterUma: $inviter@uma.domain - inviteeUma: $invitee@uma.domain - status: CLAIMED - url: https://uma.me/i/019542f5 - timestamp: '2025-09-01T15:45:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: INVITATION_CLAIMED - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - kyc-status: - post: - summary: KYC customer status change - description: | - Webhook that is called when the KYC status of a customer is updated. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid API public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - ### KYC/B Flow - This webhook is triggered when KYC/B has reached a decision on a customer. Generally most customers will finish KYC within a few minutes. Others might be rejected because of incorrect data passed in or may have been flagged for manual review. - The webhook will only trigger for final states. This will be APPROVED, REJECTED, EXPIRED, CANCELED, MANUALLY_APPROVED, MANUALLY_REJECTED. - - * APPROVED: The customer has been approved. - * REJECTED: The customer has been rejected after a KYC check. - * PENDING_REVIEW: KYC check is in progress. - * EXPIRED: KYC check has expired. This is generally because a customer did not submit all required information needed within a session. - * CANCELED: KYC check was canceled. - * MANUALLY_APPROVED: The customer was manually approved. - * MANUALLY_REJECTED: The customer was manually rejected. - * NOT_STARTED: KYC has not started on the customer. - operationId: kycStatusWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/KycStatusWebhook' - examples: - kycApprovedWebhook: - summary: When a customer KYC has been approved - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - kycStatus: APPROVED - type: KYC_STATUS - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - responses: - '200': - description: | - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - account-status: - post: - summary: Account status notification webhook - description: | - Webhook that is called when the balance of an account changes - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - ### Account status - When the balance of an internal account changes, we will push a notification with information on the account, the new balance, and who the account belongs to. - operationId: accountStatusWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AccountStatusWebhook' - examples: - balanceDecrease: - summary: A transaction just cleared a customer account and the balance has decreased - value: - account: - accountId: Account:019542f5-b3e7-1d02-0000-000000000005 - oldBalance: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - newBalance: - amount: 10000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 019542f5-b3e7-1d02-0000-000000000001 - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: ACCOUNT_STATUS - responses: - '200': - description: | - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' -components: - securitySchemes: - BasicAuth: - type: http - scheme: basic - description: API token authentication using format `:` - WebhookSignature: - type: apiKey - in: header - name: X-Grid-Signature - description: | - Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to verify that the webhook was sent by Grid. - - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - schemas: - AllErrors: - anyOf: - - $ref: '#/components/schemas/Error400' - - $ref: '#/components/schemas/Error401' - - $ref: '#/components/schemas/Error403' - - $ref: '#/components/schemas/Error404' - - $ref: '#/components/schemas/Error409' - - $ref: '#/components/schemas/Error410' - - $ref: '#/components/schemas/Error412' - - $ref: '#/components/schemas/Error424' - - $ref: '#/components/schemas/Error500' - - $ref: '#/components/schemas/Error501' - CustomerInfoFieldName: - type: string - enum: - - FULL_NAME - - BIRTH_DATE - - NATIONALITY - - PHONE_NUMBER - - EMAIL - - POSTAL_ADDRESS - - TAX_ID - - REGISTRATION_NUMBER - - USER_TYPE - - COUNTRY_OF_RESIDENCE - - ACCOUNT_IDENTIFIER - - FI_LEGAL_ENTITY_NAME - - FI_ADDRESS - - PURPOSE_OF_PAYMENT - - ULTIMATE_INSTITUTION_COUNTRY - - IDENTIFIER - description: Name of a type of field containing info about a platform's customer or counterparty customer. - example: FULL_NAME - CounterpartyFieldDefinition: - type: object - properties: - name: - $ref: '#/components/schemas/CustomerInfoFieldName' - mandatory: - type: boolean - description: Whether the field is mandatory - example: true - required: - - name - - mandatory - TransactionType: - type: string - enum: - - INCOMING - - OUTGOING - description: Type of transaction (incoming payment or outgoing payment) - PlatformCurrencyConfig: - type: object - properties: - currencyCode: - type: string - description: Three-letter currency code (ISO 4217) - example: USD - minAmount: - type: integer - format: int64 - description: Minimum amount that can be sent in the smallest unit of this currency - minimum: 0 - example: 100 - maxAmount: - type: integer - format: int64 - description: Maximum amount that can be sent in the smallest unit of this currency - minimum: 0 - example: 1000000 - requiredCounterpartyFields: - type: array - items: - $ref: '#/components/schemas/CounterpartyFieldDefinition' - description: List of fields which the platform requires from the counterparty institutions about counterparty customers. Platforms can set mandatory to false if the platform does not require the field, but would like to have it available. Some fields may be required by the underlying UMA provider. - example: - - name: FULL_NAME - mandatory: true - - name: BIRTH_DATE - mandatory: true - - name: NATIONALITY - mandatory: true - providerRequiredCustomerFields: - type: array - items: - $ref: '#/components/schemas/CustomerInfoFieldName' - description: List of customer info field names that are required by the underlying UMA provider when creating a customer for this currency. These fields must be supplied when creating or updating a customer if this currency is intended to be used by that customer. If no fields are required, this field is omitted. - readOnly: true - example: - - NATIONALITY - - BIRTH_DATE - providerRequiredCounterpartyCustomerFields: - type: array - items: - $ref: '#/components/schemas/CustomerInfoFieldName' - description: List of fields that are required by the underlying UMA provider for this currency. If the counterparty does not provide these fields, quote requests will fail. - readOnly: true - example: - - FULL_NAME - - COUNTRY_OF_RESIDENCE - enabledTransactionTypes: - type: array - items: - $ref: '#/components/schemas/TransactionType' - description: List of transaction types that are enabled for this currency. - example: - - OUTGOING - - INCOMING - required: - - currencyCode - - minAmount - - maxAmount - - requiredCounterpartyFields - - enabledTransactionTypes - PlatformConfig: - type: object - properties: - id: - type: string - description: System-generated unique identifier - readOnly: true - example: PlatformConfig:019542f5-b3e7-1d02-0000-000000000003 - umaDomain: - type: string - description: UMA domain for this platform - example: platform.uma.domain - proxyUmaSubdomain: - type: string - description: The subdomain that incoming requests will be proxied to - example: platform - webhookEndpoint: - type: string - description: URL where webhook notifications will be sent - example: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - type: array - items: - $ref: '#/components/schemas/PlatformCurrencyConfig' - description: | - List of currencies supported by the platform. This is what the platform's - customers are able to hold, send, and receive. - isRegulatedFinancialInstitution: - type: boolean - description: | - Whether the platform is a regulated financial institution. This is used to - determine if the platform's customers must be KYC/KYB'd by Lightspark via - the KYC link flow. This can only be set by Lightspark during platform - creation. - example: false - createdAt: - type: string - format: date-time - description: Creation timestamp - readOnly: true - example: '2025-06-15T12:30:45Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - readOnly: true - example: '2025-06-15T12:30:45Z' - Error401: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 401 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNAUTHORIZED | Issue with API credentials | - | INVALID_SIGNATURE | Signature header is invalid | - enum: - - UNAUTHORIZED - - INVALID_SIGNATURE - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error500: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 500 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | GRID_SWITCH_ERROR | Grid switch error | - | INTERNAL_ERROR | Internal server or UMA error | - enum: - - GRID_SWITCH_ERROR - - INTERNAL_ERROR - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error400: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 400 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | INVALID_INPUT | Invalid input provided | - | MISSING_MANDATORY_USER_INFO | Required customer information is missing | - | INVITATION_ALREADY_CLAIMED | Invitation has already been claimed | - | INVITATIONS_NOT_CONFIGURED | Invitations are not configured | - | INVALID_UMA_ADDRESS | UMA address format is invalid | - | INVITATION_CANCELLED | Invitation has been cancelled | - | QUOTE_REQUEST_FAILED | An issue occurred during the quote process; this is retryable | - | INVALID_PAYREQ_RESPONSE | Counterparty Payreq response was invalid | - | INVALID_RECEIVER | Receiver is invalid | - | PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq response | - | CERT_CHAIN_INVALID | Counterparty certificate chain is invalid | - | CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired | - | INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid | - | MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA parameters are missing | - | SENDER_NOT_ACCEPTED | Sender is not accepted | - | AMOUNT_OUT_OF_RANGE | Amount is out of range | - | INVALID_CURRENCY | Currency is invalid | - | INVALID_TIMESTAMP | Timestamp is invalid | - | INVALID_NONCE | Nonce is invalid | - | INVALID_REQUEST_FORMAT | Request format is invalid | - | INVALID_BANK_ACCOUNT | Bank account is invalid | - | SELF_PAYMENT | Self payment not allowed | - | LOOKUP_REQUEST_FAILED | Lookup request failed | - | PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response | - | INVALID_AMOUNT | Amount is invalid | - | WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set | - | WEBHOOK_DELIVERY_ERROR | Webhook delivery error | - enum: - - INVALID_INPUT - - MISSING_MANDATORY_USER_INFO - - INVITATION_ALREADY_CLAIMED - - INVITATIONS_NOT_CONFIGURED - - INVALID_UMA_ADDRESS - - INVITATION_CANCELLED - - QUOTE_REQUEST_FAILED - - INVALID_PAYREQ_RESPONSE - - INVALID_RECEIVER - - PARSE_PAYREQ_RESPONSE_ERROR - - CERT_CHAIN_INVALID - - CERT_CHAIN_EXPIRED - - INVALID_PUBKEY_FORMAT - - MISSING_REQUIRED_UMA_PARAMETERS - - SENDER_NOT_ACCEPTED - - AMOUNT_OUT_OF_RANGE - - INVALID_CURRENCY - - INVALID_TIMESTAMP - - INVALID_NONCE - - INVALID_REQUEST_FORMAT - - INVALID_BANK_ACCOUNT - - SELF_PAYMENT - - LOOKUP_REQUEST_FAILED - - PARSE_LNURLP_RESPONSE_ERROR - - INVALID_AMOUNT - - WEBHOOK_ENDPOINT_NOT_SET - - WEBHOOK_DELIVERY_ERROR - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error501: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 501 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY | Unrecognized mandatory payee data key | - | NOT_IMPLEMENTED | Feature not implemented | - enum: - - UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY - - NOT_IMPLEMENTED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - CustomerType: - type: string - enum: - - INDIVIDUAL - - BUSINESS - description: Whether the customer is an individual or a business entity - example: INDIVIDUAL - IndividualCustomer: - allOf: - - $ref: '#/components/schemas/Customer' - - $ref: '#/components/schemas/IndividualCustomerFields' - Customer: - type: object - required: - - umaAddress - - platformCustomerId - - customerType - properties: - id: - type: string - description: System-generated unique identifier - readOnly: true - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: Platform-specific customer identifier - example: 9f84e0c2a72c4fa - customerType: - $ref: '#/components/schemas/CustomerType' - kycStatus: - $ref: '#/components/schemas/KycStatus' - umaAddress: - type: string - description: Full UMA address (always present in responses, even if system-generated). This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com - createdAt: - type: string - format: date-time - description: Creation timestamp - readOnly: true - example: '2025-07-21T17:32:28Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - readOnly: true - example: '2025-07-21T17:32:28Z' - isDeleted: - type: boolean - description: Whether the customer is marked as deleted - example: false - readOnly: true - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - Address: - type: object - required: - - line1 - - postalCode - - country - properties: - line1: - type: string - description: Street address line 1 - example: 123 Main Street - line2: - type: string - description: Street address line 2 - example: Apt 4B - city: - type: string - description: City - example: San Francisco - state: - type: string - description: State/Province/Region - example: CA - postalCode: - type: string - description: Postal/ZIP code - example: '94105' - country: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - BusinessInfoUpdate: - type: object - description: Additional information for business entities - properties: - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 - UltimateBeneficialOwner: - type: object - required: - - fullName - - individualType - properties: - fullName: - type: string - description: Individual's full name - example: John Michael Doe - emailAddress: - type: string - format: email - description: Email address of the individual - example: example@test.com - phoneNumber: - type: string - description: Phone number of the individual in E.164 format - example: '+5555555555' - pattern: ^\+[1-9]\d{1,14}$ - taxId: - type: string - description: Tax identification number of the individual. This could be a Social Security Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or a Passport Number. - example: EIN-987654321 - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - address: - $ref: '#/components/schemas/Address' - individualType: - type: string - enum: - - DIRECTOR - - CONTROL_PERSON - - BUSINESS_POINT_OF_CONTACT - - TRUSTEE - - SETTLOR - - GENERAL_PARTNER - description: Type of individual in the corporation - example: DIRECTOR - percentageOwnership: - type: number - description: Percent of ownership when individual type is beneficial owner - example: 25 - title: - type: string - description: Title at company - example: CEO, COO, President - BusinessCustomerFields: - type: object - required: - - customerType - properties: - customerType: - type: string - enum: - - BUSINESS - address: - $ref: '#/components/schemas/Address' - businessInfo: - $ref: '#/components/schemas/BusinessInfoUpdate' - beneficialOwners: - type: array - items: - $ref: '#/components/schemas/UltimateBeneficialOwner' - BusinessInfo: - type: object - description: Additional information required for business entities - required: - - legalName - properties: - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 - BusinessCustomer: - allOf: - - $ref: '#/components/schemas/Customer' - - $ref: '#/components/schemas/BusinessCustomerFields' - - type: object - properties: - businessInfo: - $ref: '#/components/schemas/BusinessInfo' - KycStatus: - type: string - enum: - - APPROVED - - REJECTED - - PENDING_REVIEW - - EXPIRED - - CANCELED - - MANUALLY_APPROVED - - MANUALLY_REJECTED - description: The current KYC status of a customer - example: APPROVED - IndividualCustomerFields: - type: object - required: - - customerType - properties: - customerType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: Individual's full name - example: John Michael Doe - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - address: - $ref: '#/components/schemas/Address' - CustomerOneOf: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - IndividualCustomerCreateRequest: - allOf: - - $ref: '#/components/schemas/CustomerCreateRequest' - - $ref: '#/components/schemas/IndividualCustomerFields' - CustomerCreateRequest: - type: object - required: - - customerType - - platformCustomerId - properties: - platformCustomerId: - type: string - description: Platform-specific customer identifier. If not provided, one will be generated by the system. - example: 9f84e0c2a72c4fa - customerType: - $ref: '#/components/schemas/CustomerType' - umaAddress: - type: string - description: Optional UMA address identifier. If not provided during customer creation, one will be generated by the system. If provided during customer update, the UMA address will be updated to the provided value. This is an optional identifier to route payments to the customer. This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerCreateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerCreateRequest' - BusinessCustomerCreateRequest: - allOf: - - $ref: '#/components/schemas/CustomerCreateRequest' - - $ref: '#/components/schemas/BusinessCustomerFields' - - type: object - properties: - businessInfo: - $ref: '#/components/schemas/BusinessInfo' - CustomerCreateRequestOneOf: - oneOf: - - title: Individual Customer Create Request - $ref: '#/components/schemas/IndividualCustomerCreateRequest' - - title: Business Customer Create Request - $ref: '#/components/schemas/BusinessCustomerCreateRequest' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerCreateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerCreateRequest' - Error409: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 409 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | - | UMA_ADDRESS_EXISTS | UMA address already exists | - enum: - - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - - UMA_ADDRESS_EXISTS - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error404: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 404 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | TRANSACTION_NOT_FOUND | Transaction not found | - | INVITATION_NOT_FOUND | Invitation not found | - | USER_NOT_FOUND | Customer not found | - | QUOTE_NOT_FOUND | Quote not found | - | LOOKUP_REQUEST_NOT_FOUND | Lookup request not found | - | TOKEN_NOT_FOUND | Token not found | - | BULK_UPLOAD_JOB_NOT_FOUND | Bulk upload job not found | - | REFERENCE_NOT_FOUND | Reference not found | - enum: - - TRANSACTION_NOT_FOUND - - INVITATION_NOT_FOUND - - USER_NOT_FOUND - - QUOTE_NOT_FOUND - - LOOKUP_REQUEST_NOT_FOUND - - TOKEN_NOT_FOUND - - BULK_UPLOAD_JOB_NOT_FOUND - - REFERENCE_NOT_FOUND - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error410: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 410 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | CUSTOMER_DELETED | Customer has been permanently deleted | - enum: - - CUSTOMER_DELETED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - IndividualCustomerUpdateRequest: - allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/IndividualCustomerFields' - CustomerUpdateRequest: - type: object - required: - - customerType - properties: - customerType: - $ref: '#/components/schemas/CustomerType' - umaAddress: - type: string - description: Optional UMA address identifier. If provided, the customer's UMA address will be updated. This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' - BusinessCustomerUpdateRequest: - allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/BusinessCustomerFields' - CustomerUpdateRequestOneOf: - oneOf: - - title: Individual Customer Update Request - $ref: '#/components/schemas/IndividualCustomerUpdateRequest' - - title: Business Customer Update Request - $ref: '#/components/schemas/BusinessCustomerUpdateRequest' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' - Currency: - type: object - properties: - code: - type: string - description: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) - example: USD - name: - type: string - description: Full name of the currency - example: United States Dollar - symbol: - type: string - description: Symbol of the currency - example: $ - decimals: - type: integer - description: Number of decimal places for the currency - minimum: 0 - example: 2 - CurrencyAmount: - type: object - required: - - amount - - currency - properties: - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - currency: - $ref: '#/components/schemas/Currency' - PaymentClabeAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/ClabeAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - BasePaymentAccountInfo: - type: object - required: - - accountType - properties: - accountType: - $ref: '#/components/schemas/PaymentAccountType' - discriminator: - propertyName: accountType - mapping: - CLABE: '#/components/schemas/PaymentClabeAccountInfo' - US_ACCOUNT: '#/components/schemas/PaymentUsAccountInfo' - PIX: '#/components/schemas/PaymentPixAccountInfo' - IBAN: '#/components/schemas/PaymentIbanAccountInfo' - UPI: '#/components/schemas/PaymentUpiAccountInfo' - NGN_ACCOUNT: '#/components/schemas/PaymentNgnAccountInfo' - SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo' - LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo' - SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo' - TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo' - POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo' - BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo' - UsAccountInfo: - type: object - required: - - accountNumber - - routingNumber - - accountCategory - - accountType - properties: - accountType: - type: string - enum: - - US_ACCOUNT - accountNumber: - type: string - description: US bank account number - example: '123456789' - routingNumber: - type: string - description: ACH routing number (9 digits) - example: '987654321' - minLength: 9 - maxLength: 9 - pattern: ^[0-9]{9}$ - accountCategory: - type: string - enum: - - CHECKING - - SAVINGS - description: Type of account (checking or savings) - example: CHECKING - bankName: - type: string - description: Name of the bank - example: Chase Bank - PaymentUsAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/UsAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - PixAccountInfo: - type: object - required: - - pixKey - - pixKeyType - - taxId - - accountType - properties: - accountType: - type: string - enum: - - PIX - pixKey: - type: string - description: PIX key for Brazilian instant payments - example: '55119876543210' - pixKeyType: - type: string - enum: - - CPF - - CNPJ - - EMAIL - - PHONE - - RANDOM - description: Type of PIX key being used - example: PHONE - taxId: - type: string - description: Tax ID of the account holder - example: '1234567890' - PaymentPixAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/PixAccountInfo' - IbanAccountInfo: - type: object - required: - - iban - - swiftBic - - accountType - properties: - accountType: - type: string - enum: - - IBAN - iban: - type: string - description: International Bank Account Number - example: DE89370400440532013000 - minLength: 15 - maxLength: 34 - swiftBic: - type: string - description: SWIFT/BIC code (8 or 11 characters) - example: DEUTDEFF - minLength: 8 - maxLength: 11 - pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - PaymentIbanAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/IbanAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - UpiAccountInfo: - type: object - required: - - vpa - - accountType - properties: - accountType: - type: string - enum: - - UPI - vpa: - type: string - description: Virtual Payment Address for UPI payments - example: somecustomers@okbank - PaymentUpiAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/UpiAccountInfo' - NgnAccountInfo: - type: object - required: - - accountNumber - - bankName - - accountType - properties: - accountType: - type: string - enum: - - NGN_ACCOUNT - accountNumber: - type: string - description: Nigerian bank account number - example: '0123456789' - minLength: 10 - maxLength: 10 - pattern: ^[0-9]{10}$ - bankName: - type: string - description: Name of the bank - example: First Bank of Nigeria - PaymentNgnAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - SparkWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - SPARK_WALLET - address: - type: string - description: Spark wallet address - example: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - PaymentSparkWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - - type: object - required: - - assetType - properties: - assetType: - type: string - description: Type of asset - enum: - - BTC - - USDB - invoice: - type: string - description: Invoice for the payment - example: sparkrt1pgss8ter0fhc4c220f3zftmpz49h8wqte8eg3m5zkrraplgc048jucgszg3ssqgjzqqekv73mmh842yj7drsjwh7t7tz5zt8wf5kghm5v4ehggszppjp5s80cg3qjdzc55g2567tn3lj705hdsr577tg8ah795mlnt6807y657qhkmgfkf9w75p4wz3l8vhua85zdn6ryj32zuj0p00pv2l5z4u47mw6h4s - PaymentLightningInvoiceInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - type: object - required: - - invoice - properties: - invoice: - type: string - description: Invoice for the payment - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - SolanaWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - SOLANA_WALLET - address: - type: string - description: Solana wallet address - example: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - PaymentSolanaWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - - USDT - TronWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - TRON_WALLET - address: - type: string - description: Tron wallet address - example: TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL - PaymentTronWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDT - PolygonWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - POLYGON_WALLET - address: - type: string - description: Polygon eth wallet address - example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' - PaymentPolygonWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - BaseWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - BASE_WALLET - address: - type: string - description: Base eth wallet address - example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' - PaymentBaseWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - PaymentAccountType: - type: string - enum: - - CLABE - - US_ACCOUNT - - PIX - - IBAN - - UPI - - NGN_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET - description: Type of payment account or wallet - example: US_ACCOUNT - ClabeAccountInfo: - type: object - required: - - clabeNumber - - accountType - properties: - accountType: - type: string - enum: - - CLABE - clabeNumber: - type: string - description: 18-digit CLABE number (Mexican banking standard) - example: '123456789012345678' - minLength: 18 - maxLength: 18 - pattern: ^[0-9]{18}$ - PaymentInstructions: - type: object - required: - - accountOrWalletInfo - properties: - instructionsNotes: - type: string - description: Additional human-readable instructions for making the payment - example: Please ensure the reference code is included in the payment memo/description field - isPlatformAccount: - type: boolean - description: Indicates whether the account is a platform account or a customer account. - example: true - accountOrWalletInfo: - oneOf: - - title: CLABE Account - $ref: '#/components/schemas/PaymentClabeAccountInfo' - - title: US Bank Account - $ref: '#/components/schemas/PaymentUsAccountInfo' - - title: PIX Account - $ref: '#/components/schemas/PaymentPixAccountInfo' - - title: IBAN Account - $ref: '#/components/schemas/PaymentIbanAccountInfo' - - title: UPI Account - $ref: '#/components/schemas/PaymentUpiAccountInfo' - - title: Spark Wallet - $ref: '#/components/schemas/PaymentSparkWalletInfo' - - title: Lightning Invoice - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - - title: Solana Wallet - $ref: '#/components/schemas/PaymentSolanaWalletInfo' - - title: Tron Wallet - $ref: '#/components/schemas/PaymentTronWalletInfo' - - title: Polygon Wallet - $ref: '#/components/schemas/PaymentPolygonWalletInfo' - - title: Base Wallet - $ref: '#/components/schemas/PaymentBaseWalletInfo' - discriminator: - propertyName: accountType - mapping: - CLABE: '#/components/schemas/PaymentClabeAccountInfo' - US_ACCOUNT: '#/components/schemas/PaymentUsAccountInfo' - PIX: '#/components/schemas/PaymentPixAccountInfo' - IBAN: '#/components/schemas/PaymentIbanAccountInfo' - UPI: '#/components/schemas/PaymentUpiAccountInfo' - SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo' - LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo' - SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo' - TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo' - POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo' - BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo' - InternalAccount: - type: object - required: - - id - - balance - - fundingPaymentInstructions - - createdAt - - updatedAt - properties: - id: - type: string - description: The ID of the internal account - example: InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - customerId: - type: string - description: The ID of the customer associated with the internal account. If this field is empty, the internal account belongs to the platform. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - balance: - $ref: '#/components/schemas/CurrencyAmount' - fundingPaymentInstructions: - type: array - description: Payment instructions for funding the account - items: - $ref: '#/components/schemas/PaymentInstructions' - createdAt: - type: string - format: date-time - description: Timestamp when the internal account was created - example: '2025-10-03T12:30:00Z' - updatedAt: - type: string - format: date-time - description: Timestamp when the internal account was last updated - example: '2025-10-03T12:30:00Z' - ExternalAccountStatus: - type: string - enum: - - PENDING - - ACTIVE - - UNDER_REVIEW - - INACTIVE - description: Status of an external account - UsAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UsAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - BaseExternalAccountInfo: - type: object - required: - - accountType - properties: - accountType: - $ref: '#/components/schemas/ExternalAccountType' - discriminator: - propertyName: accountType - mapping: - US_ACCOUNT: '#/components/schemas/UsAccountExternalAccountInfo' - CLABE: '#/components/schemas/ClabeAccountExternalAccountInfo' - PIX: '#/components/schemas/PixAccountExternalAccountInfo' - IBAN: '#/components/schemas/IbanAccountExternalAccountInfo' - UPI: '#/components/schemas/UpiAccountExternalAccountInfo' - NGN_ACCOUNT: '#/components/schemas/NgnAccountExternalAccountInfo' - CAD_ACCOUNT: '#/components/schemas/CadAccountExternalAccountInfo' - GBP_ACCOUNT: '#/components/schemas/GbpAccountExternalAccountInfo' - PHP_ACCOUNT: '#/components/schemas/PhpAccountExternalAccountInfo' - SGD_ACCOUNT: '#/components/schemas/SgdAccountExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' - LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' - BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' - IndividualBeneficiary: - allOf: - - $ref: '#/components/schemas/BaseBeneficiary' - - type: object - required: - - fullName - - birthDate - - nationality - - beneficiaryType - properties: - beneficiaryType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: Individual's full name - example: John Michael Doe - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - BaseBeneficiary: - type: object - required: - - beneficiaryType - properties: - beneficiaryType: - $ref: '#/components/schemas/BeneficiaryType' - address: - $ref: '#/components/schemas/Address' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - BusinessBeneficiary: - allOf: - - $ref: '#/components/schemas/BaseBeneficiary' - - type: object - required: - - legalName - - beneficiaryType - properties: - beneficiaryType: - type: string - enum: - - BUSINESS - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 - BeneficiaryType: - type: string - enum: - - INDIVIDUAL - - BUSINESS - description: Whether the beneficiary is an individual or a business entity - example: INDIVIDUAL - BeneficiaryOneOf: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/IndividualBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - ClabeAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ClabeAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - PixAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PixAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - IbanAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/IbanAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - UpiAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UpiAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - NgnAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' - - type: object - required: - - purposeOfPayment - - beneficiary - properties: - purposeOfPayment: - type: string - enum: - - GIFT - - SELF - - GOODS_OR_SERVICES - - EDUCATION - - HEALTH_OR_MEDICAL - - REAL_ESTATE_PURCHASE - - LOAN_PAYMENT - - TAX_PAYMENT - - UTILITY_BILL - - DONATION - - TRAVEL - - OTHER - description: Purpose of payment - example: GOODS_OR_SERVICES - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - CadAccountInfo: - type: object - required: - - bankCode - - branchCode - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - CAD_ACCOUNT - bankCode: - type: string - description: Canadian financial institution number (3 digits) - example: '001' - minLength: 3 - maxLength: 3 - pattern: ^[0-9]{3}$ - branchCode: - type: string - description: Transit number identifying the branch (5 digits) - example: '00012' - minLength: 5 - maxLength: 5 - pattern: ^[0-9]{5}$ - accountNumber: - type: string - description: Bank account number (7-12 digits) - example: '1234567' - minLength: 7 - maxLength: 12 - pattern: ^[0-9]{7,12}$ - CadAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - GbpAccountInfo: - type: object - required: - - sortCode - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - GBP_ACCOUNT - sortCode: - type: string - description: UK bank sort code (6 digits, may include hyphens) - example: 20-00-00 - pattern: ^[0-9]{2}-?[0-9]{2}-?[0-9]{2}$ - accountNumber: - type: string - description: UK bank account number (8 digits) - example: '12345678' - minLength: 8 - maxLength: 8 - pattern: ^[0-9]{8}$ - GbpAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - PhpAccountInfo: - type: object - required: - - bankName - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - PHP_ACCOUNT - bankName: - type: string - description: Name of the beneficiary's bank - example: BDO Unibank - accountNumber: - type: string - description: Bank account number - example: '001234567890' - PhpAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PhpAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - SgdAccountInfo: - type: object - required: - - bankName - - swiftCode - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - SGD_ACCOUNT - bankName: - type: string - description: Name of the beneficiary's bank - example: DBS Bank Ltd - swiftCode: - type: string - description: SWIFT/BIC code (8 or 11 characters) - example: DBSSSGSG - minLength: 8 - maxLength: 11 - pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - accountNumber: - type: string - description: Bank account number - example: '0123456789' - SgdAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SgdAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - SparkWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - LightningExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - type: object - description: | - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. - required: - - accountType - properties: - accountType: - type: string - enum: - - LIGHTNING - invoice: - type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: - type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: - type: string - description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. - example: john.doe@lightningwallet.com - SolanaWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - TronWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' - PolygonWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - BaseWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - ExternalAccountType: - type: string - enum: - - US_ACCOUNT - - CLABE - - PIX - - IBAN - - UPI - - NGN_ACCOUNT - - CAD_ACCOUNT - - GBP_ACCOUNT - - PHP_ACCOUNT - - SGD_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET - description: Type of external account or wallet - example: US_ACCOUNT - ExternalAccountInfoOneOf: - oneOf: - - title: US Account - $ref: '#/components/schemas/UsAccountExternalAccountInfo' - - title: CLABE Account - $ref: '#/components/schemas/ClabeAccountExternalAccountInfo' - - title: PIX Account - $ref: '#/components/schemas/PixAccountExternalAccountInfo' - - title: IBAN Account - $ref: '#/components/schemas/IbanAccountExternalAccountInfo' - - title: UPI Account - $ref: '#/components/schemas/UpiAccountExternalAccountInfo' - - title: NGN Account - $ref: '#/components/schemas/NgnAccountExternalAccountInfo' - - title: CAD Account - $ref: '#/components/schemas/CadAccountExternalAccountInfo' - - title: GBP Account - $ref: '#/components/schemas/GbpAccountExternalAccountInfo' - - title: PHP Account - $ref: '#/components/schemas/PhpAccountExternalAccountInfo' - - title: SGD Account - $ref: '#/components/schemas/SgdAccountExternalAccountInfo' - - title: Spark Wallet - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - - title: Lightning - $ref: '#/components/schemas/LightningExternalAccountInfo' - - title: Solana Wallet - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - - title: Tron Wallet - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - - title: Polygon Wallet - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - - title: Base Wallet - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' - discriminator: - propertyName: accountType - mapping: - US_ACCOUNT: '#/components/schemas/UsAccountExternalAccountInfo' - CLABE: '#/components/schemas/ClabeAccountExternalAccountInfo' - PIX: '#/components/schemas/PixAccountExternalAccountInfo' - IBAN: '#/components/schemas/IbanAccountExternalAccountInfo' - UPI: '#/components/schemas/UpiAccountExternalAccountInfo' - NGN_ACCOUNT: '#/components/schemas/NgnAccountExternalAccountInfo' - CAD_ACCOUNT: '#/components/schemas/CadAccountExternalAccountInfo' - GBP_ACCOUNT: '#/components/schemas/GbpAccountExternalAccountInfo' - PHP_ACCOUNT: '#/components/schemas/PhpAccountExternalAccountInfo' - SGD_ACCOUNT: '#/components/schemas/SgdAccountExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' - LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' - BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' - ExternalAccount: - allOf: - - type: object - required: - - id - - status - - currency - - accountInfo - properties: - id: - type: string - description: The system generated identifier of this account - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - customerId: - type: string - description: The customer this account is tied to, or null if the account is on behalf of the platform. - example: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7 - status: - $ref: '#/components/schemas/ExternalAccountStatus' - description: Status of the external account - example: ACTIVE - platformAccountId: - type: string - description: Optional platform-specific identifier for this account - example: acc_123456789 - currency: - type: string - description: The ISO 4217 currency code - example: USD - defaultUmaDepositAccount: - type: boolean - description: Whether this account is the default UMA deposit account for the customer. If true, incoming UMA payments to this customer's UMA address will be automatically deposited into this account instead of the primary internal account. False if not provided. Note that at most, one external account can be set as the default UMA deposit account for a customer. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. - example: false - accountInfo: - $ref: '#/components/schemas/ExternalAccountInfoOneOf' - ExternalAccountCreateRequest: - allOf: - - type: object - required: - - currency - - accountInfo - properties: - customerId: - type: string - description: The ID of the customer for whom to create the external account. If not provided, the external account will be created on behalf of the platform. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: - type: string - description: The ISO 4217 currency code - example: USD - platformAccountId: - type: string - description: Your platform's identifier for the account in your system. This can be used to reference the account by your own identifier. - example: ext_acc_123456 - defaultUmaDepositAccount: - type: boolean - description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. - default: false - accountInfo: - $ref: '#/components/schemas/ExternalAccountInfoOneOf' - PlaidLinkTokenRequest: - type: object - required: - - customerId - properties: - customerId: - type: string - description: The ID of the customer for whom to create the Plaid Link token and external account - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - PlaidLinkTokenResponse: - type: object - required: - - linkToken - - expiration - - callbackUrl - properties: - linkToken: - type: string - description: | - The Plaid Link token to be used to initialize Plaid Link in your application. This token is single-use and expires after the specified expiration time. - example: link-sandbox-af1a0311-da53-4636-b754-dd15cc058176 - expiration: - type: string - format: date-time - description: | - The ISO 8601 timestamp when this link token expires. Link tokens typically expire after 4 hours. - example: '2025-10-05T18:30:00Z' - callbackUrl: - type: string - description: | - The URL where the platform should POST the public_token after the customer completes Plaid Link authentication. This will trigger asynchronous external account creation. The URL includes the linkToken as the path parameter. - example: https://api.lightspark.com/grid/2025-10-13/plaid/callback/{plaid_link_token} - requestId: - type: string - description: A unique identifier for this request, useful for debugging - example: req_abc123def456 - PlaidCallbackRequest: - type: object - required: - - publicToken - properties: - publicToken: - type: string - description: | - The public token returned by Plaid Link after the customer successfully - authenticates and selects an account. - example: public-sandbox-12345678-1234-1234-1234-123456789012 - accountId: - type: string - description: | - Optional Plaid account ID if the customer selected a specific account. - If not provided, the default account will be used. - example: plaid_account_id_123 - Transaction: - type: object - required: - - id - - status - - type - - destination - - customerId - - platformCustomerId - properties: - id: - type: string - description: Unique identifier for the transaction - example: Transaction:019542f5-b3e7-1d02-0000-000000000004 - status: - $ref: '#/components/schemas/TransactionStatus' - type: - $ref: '#/components/schemas/TransactionType' - destination: - $ref: '#/components/schemas/TransactionDestinationOneOf' - customerId: - type: string - description: System ID of the customer (sender for outgoing, recipient for incoming) - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: Platform-specific ID of the customer (sender for outgoing, recipient for incoming) - example: 18d3e5f7b4a9c2 - settledAt: - type: string - format: date-time - description: When the payment was or will be settled - example: '2025-08-15T14:30:00Z' - createdAt: - type: string - format: date-time - description: When the transaction was created - example: '2025-08-15T14:25:18Z' - updatedAt: - type: string - format: date-time - description: When the transaction was last updated - example: '2025-08-15T14:30:00Z' - description: - type: string - description: Optional memo or description for the payment - example: 'Payment for invoice #1234' - counterpartyInformation: - type: object - description: Additional information about the counterparty, if available and relevant to the transaction and platform. Only applicable for transactions to/from UMA addresses. - additionalProperties: true - example: - FULL_NAME: John Sender - BIRTH_DATE: '1985-06-15' - NATIONALITY: DE - discriminator: - propertyName: type - mapping: - INCOMING: '#/components/schemas/IncomingTransaction' - OUTGOING: '#/components/schemas/OutgoingTransaction' - AccountTransactionSource: - allOf: - - $ref: '#/components/schemas/BaseTransactionSource' - - type: object - required: - - accountId - - sourceType - properties: - sourceType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Source account identifier - example: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Source account details - BaseTransactionSource: - type: object - required: - - sourceType - properties: - sourceType: - $ref: '#/components/schemas/TransactionSourceType' - currency: - type: string - description: Currency code for the source - example: USD - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionSource' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionSource' - UmaAddressTransactionSource: - allOf: - - $ref: '#/components/schemas/BaseTransactionSource' - - type: object - required: - - umaAddress - - sourceType - properties: - sourceType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the sender - example: $sender@uma.domain.com - description: UMA address source details - TransactionSourceType: - type: string - enum: - - ACCOUNT - - UMA_ADDRESS - - REALTIME_FUNDING - description: Type of transaction source - example: ACCOUNT - TransactionSourceOneOf: - oneOf: - - title: Account Source - $ref: '#/components/schemas/AccountTransactionSource' - - title: UMA Address Source - $ref: '#/components/schemas/UmaAddressTransactionSource' - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionSource' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionSource' - ReconciliationInstructions: - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to match it with the correct incoming transaction - example: UMA-Q12345-REF - IncomingRateDetails: - description: Details about the rate and fees for an incoming transaction. - type: object - required: - - gridApiMultiplier - - gridApiFixedFee - - gridApiVariableFeeRate - - gridApiVariableFeeAmount - properties: - gridApiMultiplier: - type: number - format: double - description: The underlying multiplier from the mSATS to the receiving currency, including variable fees. - exclusiveMinimum: 0 - example: 0.925 - gridApiFixedFee: - type: integer - format: int64 - description: The fixed fee charged by the Grid product to execute the quote in the smallest unit of the receiving currency (eg. cents). - minimum: 0 - example: 10 - gridApiVariableFeeRate: - type: number - format: double - description: The variable fee rate charged by the Grid product to execute the quote as a percentage of the receiving currency amount. - exclusiveMinimum: 0 - example: 0.003 - gridApiVariableFeeAmount: - type: number - format: int64 - description: The variable fee amount charged by the Grid product to execute the quote in the smallest unit of the receiving currency (eg. cents). This is the receiving amount times gridApiVariableFeeRate. - minimum: 0 - example: 30 - IncomingTransactionFailureReason: - type: string - enum: - - LNURLP_FAILED - - PAY_REQUEST_FAILED - - PAYMENT_APPROVAL_WEBHOOK_ERROR - - PAYMENT_APPROVAL_TIMED_OUT - - OFFRAMP_FAILED - - MISSING_MANDATORY_PAYEE_DATA - - QUOTE_EXPIRED - - QUOTE_EXECUTION_FAILED - description: Reason for failure of an incoming transaction. This is used to provide more context on why a transaction failed. If the transaction is not in a failed state, this field is omitted. - IncomingTransaction: - allOf: - - $ref: '#/components/schemas/Transaction' - - type: object - required: - - receivedAmount - properties: - source: - $ref: '#/components/schemas/TransactionSourceOneOf' - receivedAmount: - $ref: '#/components/schemas/CurrencyAmount' - description: Amount received in the recipient's currency - reconciliationInstructions: - $ref: '#/components/schemas/ReconciliationInstructions' - description: Included for all transactions except those with "CREATED" status - rateDetails: - $ref: '#/components/schemas/IncomingRateDetails' - description: Details about the rate and fees for the transaction. - failureReason: - $ref: '#/components/schemas/IncomingTransactionFailureReason' - description: If the transaction failed, this field provides the reason for failure. - Refund: - type: object - required: - - reference - - initiatedAt - properties: - reference: - type: string - description: The unique reference code of the refund - example: UMA-Q12345-REFUND - initiatedAt: - type: string - format: date-time - description: When the refund was initiated - example: '2025-08-15T14:30:00Z' - settledAt: - type: string - format: date-time - description: When the refund was or will be settled - example: '2025-08-15T14:30:00Z' - OutgoingRateDetails: - description: Details about the rate and fees for an outgoing transaction or quote. - type: object - required: - - counterpartyMultiplier - - counterpartyFixedFee - - gridApiMultiplier - - gridApiFixedFee - - gridApiVariableFeeRate - - gridApiVariableFeeAmount - properties: - counterpartyMultiplier: - type: number - format: double - description: The underlying multiplier from mSATs to the receiving currency as returned by the counterparty institution. - exclusiveMinimum: 0 - example: 1.08 - counterpartyFixedFee: - type: integer - format: int64 - description: The fixed fee charged by the counterparty institution to execute the quote in the smallest unit of the receiving currency (eg. cents). - minimum: 0 - example: 10 - gridApiMultiplier: - type: number - format: double - description: The underlying multiplier from the sending currency to mSATS, including variable fees. - exclusiveMinimum: 0 - example: 0.925 - gridApiFixedFee: - type: integer - format: int64 - description: The fixed fee charged by the Grid product to execute the quote in the smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - gridApiVariableFeeRate: - type: number - format: double - description: The variable fee rate charged by the Grid product to execute the quote as a percentage of the sending currency amount. - exclusiveMinimum: 0 - example: 0.003 - gridApiVariableFeeAmount: - type: number - format: int64 - description: The variable fee amount charged by the Grid product to execute the quote in the smallest unit of the sending currency (eg. cents). This is the sending amount times gridApiVariableFeeRate. - minimum: 0 - example: 30 - OutgoingTransactionFailureReason: - type: string - enum: - - QUOTE_EXPIRED - - QUOTE_EXECUTION_FAILED - - LIGHTNING_PAYMENT_FAILED - - FUNDING_AMOUNT_MISMATCH - - COUNTERPARTY_POST_TX_FAILED - - TIMEOUT - description: Reason for failure of an outgoing transaction. This is used to provide more context on why a transaction failed. If the transaction is not in a failed state, this field is omitted. - OutgoingTransaction: - allOf: - - $ref: '#/components/schemas/Transaction' - - type: object - required: - - sentAmount - - paymentInstructions - - source - properties: - source: - $ref: '#/components/schemas/TransactionSourceOneOf' - sentAmount: - $ref: '#/components/schemas/CurrencyAmount' - description: Amount sent in the sender's currency - receivedAmount: - $ref: '#/components/schemas/CurrencyAmount' - description: Amount to be received by recipient in the recipient's currency - exchangeRate: - type: number - description: Number of sending currency units per receiving currency unit. - exclusiveMinimum: 0 - example: 1.08 - fees: - type: integer - format: int64 - description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - quoteId: - type: string - description: The ID of the quote that was used to trigger this payment - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - originalTransactionId: - type: string - description: ID of the original transaction that this transaction is retrying, if applicable - example: Transaction:019542f5-b3e7-1d02-0000-000000000003 - paymentInstructions: - type: array - description: Payment instructions for executing the payment. - items: - $ref: '#/components/schemas/PaymentInstructions' - example: - - accountType: US_ACCOUNT - accountNumber: '1234567890' - routingNumber: '021000021' - bankName: Chase Bank - referenceCode: REF123456 - - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - invoice: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - refund: - $ref: '#/components/schemas/Refund' - description: The refund if transaction was refunded. - rateDetails: - $ref: '#/components/schemas/OutgoingRateDetails' - description: Details about the rate and fees for the transaction. - failureReason: - $ref: '#/components/schemas/OutgoingTransactionFailureReason' - description: If the transaction failed, this field provides the reason for failure. - TransactionStatus: - type: string - enum: - - CREATED - - PENDING - - PROCESSING - - COMPLETED - - REJECTED - - FAILED - - REFUNDED - - EXPIRED - description: Status of a payment transaction - AccountTransactionDestination: - allOf: - - $ref: '#/components/schemas/BaseTransactionDestination' - - type: object - required: - - accountId - - destinationType - properties: - destinationType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Destination account identifier - example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination account details - BaseTransactionDestination: - type: object - required: - - destinationType - properties: - destinationType: - $ref: '#/components/schemas/TransactionDestinationType' - currency: - type: string - description: Currency code for the destination - example: EUR - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionDestination' - UmaAddressTransactionDestination: - allOf: - - $ref: '#/components/schemas/BaseTransactionDestination' - - type: object - required: - - umaAddress - - destinationType - properties: - destinationType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the recipient - example: $receiver@uma.domain.com - description: UMA address destination details - TransactionDestinationType: - type: string - enum: - - ACCOUNT - - UMA_ADDRESS - description: Type of transaction destination - example: ACCOUNT - TransactionDestinationOneOf: - oneOf: - - title: Account Destination - $ref: '#/components/schemas/AccountTransactionDestination' - - title: UMA Address Destination - $ref: '#/components/schemas/UmaAddressTransactionDestination' - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionDestination' - CurrencyPreference: - type: object - required: - - currency - - estimatedExchangeRate - - min - - max - properties: - currency: - $ref: '#/components/schemas/Currency' - estimatedExchangeRate: - type: number - description: An estimated exchange rate from the sender's currency to this currency. This is not a locked rate and is subject to change when calling the quotes endpoint. - exclusiveMinimum: 0 - example: 1.08 - min: - type: integer - format: int64 - description: The minimum amount that can be received in this currency. - exclusiveMinimum: 0 - example: 1 - max: - type: integer - format: int64 - description: The maximum amount that can be received in this currency. - exclusiveMinimum: 0 - example: 1000000 - ReceiverLookupResponse: - type: object - required: - - supportedCurrencies - - lookupId - properties: - supportedCurrencies: - type: array - description: List of currencies supported by the receiving account - items: - $ref: '#/components/schemas/CurrencyPreference' - requiredPayerDataFields: - type: array - description: Fields required by the receiving institution about the payer before payment can be completed - items: - $ref: '#/components/schemas/CounterpartyFieldDefinition' - lookupId: - type: string - description: Unique identifier for the lookup. Needed in the subsequent create quote request. - example: Lookup:019542f5-b3e7-1d02-0000-000000000009 - Error412: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 412 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNSUPPORTED_UMA_VERSION | Counterparty doesn't support the Grid UMA version | - enum: - - UNSUPPORTED_UMA_VERSION - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error424: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - AccountQuoteSource: - allOf: - - $ref: '#/components/schemas/BaseQuoteSource' - - type: object - required: - - accountId - - sourceType - properties: - sourceType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Source account identifier - example: InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - customerId: - type: string - description: Required when funding from an FBO account to identify the customer on whose behalf the transaction is being initiated. Otherwise, will default to the customerId of the account owner. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - description: Source account details - BaseQuoteSource: - type: object - required: - - sourceType - properties: - sourceType: - $ref: '#/components/schemas/QuoteSourceType' - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountQuoteSource' - REALTIME_FUNDING: '#/components/schemas/RealtimeFundingQuoteSource' - RealtimeFundingQuoteSource: - allOf: - - $ref: '#/components/schemas/BaseQuoteSource' - - type: object - required: - - currency - - sourceType - properties: - sourceType: - type: string - enum: - - REALTIME_FUNDING - customerId: - type: string - description: Source customer ID. If this transaction is being initiated on behalf of a customer, this is required. If customerId is not provided, the quote will be created on behalf of the platform itself. - example: Customer:019542f5-b3e7-1d02-0000-000000000009 - currency: - type: string - description: Currency code for the funding source. See [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) for the full list of supported fiat and crypto currencies. - example: USD - description: Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). This will require manual just-in-time funding using `paymentInstructions` in the response. Because quotes expire quickly, this option is only valid for instant payment methods. Do not try to fund a quote with a non-instant payment method (ACH, etc.). - QuoteSourceType: - type: string - enum: - - ACCOUNT - - REALTIME_FUNDING - description: Type of quote funding source - example: ACCOUNT - QuoteSourceOneOf: - oneOf: - - title: Account - $ref: '#/components/schemas/AccountQuoteSource' - - title: Real-time Funding - $ref: '#/components/schemas/RealtimeFundingQuoteSource' - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountQuoteSource' - REALTIME_FUNDING: '#/components/schemas/RealtimeFundingQuoteSource' - AccountDestination: - allOf: - - $ref: '#/components/schemas/BaseDestination' - - type: object - required: - - accountId - - destinationType - properties: - destinationType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Destination account identifier - example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination account details - BaseDestination: - type: object - required: - - destinationType - properties: - destinationType: - $ref: '#/components/schemas/DestinationType' - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressDestination' - EXTERNAL_ACCOUNT_DETAILS: '#/components/schemas/ExternalAccountDetailsDestination' - UmaAddressDestination: - allOf: - - $ref: '#/components/schemas/BaseDestination' - - type: object - required: - - umaAddress - - destinationType - properties: - destinationType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the recipient - example: $receiver@uma.domain.com - counterpartyInformation: - type: object - description: Information about the recipient, as required by the platform in their configuration. - additionalProperties: true - example: - FULL_NAME: Jane Receiver - BIRTH_DATE: '1990-01-01' - NATIONALITY: FR - currency: - type: string - description: Currency code for the destination. See [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) for the full list of supported fiat and crypto currencies. - example: EUR - description: UMA address destination details - ExternalAccountDetailsDestination: - allOf: - - $ref: '#/components/schemas/BaseDestination' - - type: object - required: - - externalAccountDetails - - destinationType - properties: - destinationType: - type: string - enum: - - EXTERNAL_ACCOUNT_DETAILS - externalAccountDetails: - $ref: '#/components/schemas/ExternalAccountCreateRequest' - description: A convenient destination option which adds the external account and creates the quote in one step rather than first needing to call /external-accounts to add the account. Useful for one-off payments to some destination. See the external accounts endpoints for test values in sandbox mode. - DestinationType: - type: string - enum: - - ACCOUNT - - UMA_ADDRESS - - EXTERNAL_ACCOUNT_DETAILS - description: Type of payment destination - example: ACCOUNT - QuoteDestinationOneOf: - oneOf: - - title: Account - $ref: '#/components/schemas/AccountDestination' - - title: UMA Address - $ref: '#/components/schemas/UmaAddressDestination' - - title: External Account Details - $ref: '#/components/schemas/ExternalAccountDetailsDestination' - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressDestination' - EXTERNAL_ACCOUNT_DETAILS: '#/components/schemas/ExternalAccountDetailsDestination' - Quote: - type: object - required: - - quoteId - - status - - expiresAt - - createdAt - - source - - destination - - sendingCurrency - - receivingCurrency - - totalSendingAmount - - totalReceivingAmount - - exchangeRate - - feesIncluded - - transactionId - properties: - quoteId: - type: string - description: Unique identifier for this quote - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - description: Current status of the quote - example: PENDING - createdAt: - type: string - format: date-time - description: When this quote was created - example: '2025-10-03T12:00:00Z' - expiresAt: - type: string - format: date-time - description: When this quote expires (typically 1-5 minutes after creation) - example: '2025-10-03T12:05:00Z' - source: - $ref: '#/components/schemas/QuoteSourceOneOf' - destination: - $ref: '#/components/schemas/QuoteDestinationOneOf' - sendingCurrency: - $ref: '#/components/schemas/Currency' - description: Currency for the sending amount - receivingCurrency: - $ref: '#/components/schemas/Currency' - description: Currency for the receiving amount - totalSendingAmount: - type: integer - format: int64 - description: The total amount that will be sent in the smallest unit of the sending currency (eg. cents). - exclusiveMinimum: 0 - example: 123010 - totalReceivingAmount: - type: integer - format: int64 - description: The total amount that will be received in the smallest unit of the receiving currency (eg. cents). - exclusiveMinimum: 0 - example: 1000 - exchangeRate: - type: number - description: Number of sending currency units per receiving currency unit. - exclusiveMinimum: 0 - feesIncluded: - type: integer - format: int64 - description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - paymentInstructions: - type: array - description: Payment instructions for executing the payment. This is not required when using an internal account source. - items: - $ref: '#/components/schemas/PaymentInstructions' - example: - - accountType: US_ACCOUNT - accountNumber: '1234567890' - routingNumber: '021000021' - bankName: Chase Bank - referenceCode: REF123456 - - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - invoice: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - transactionId: - type: string - description: The ID of the transaction created from this quote. - example: Transaction:019542f5-b3e7-1d02-0000-000000000005 - originalQuoteId: - description: ID of the quote that is being retried - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - rateDetails: - $ref: '#/components/schemas/OutgoingRateDetails' - description: Details about the rate and fees for the transaction. - QuoteLockSide: - type: string - enum: - - SENDING - - RECEIVING - description: The side of the quote which should be locked and specified in the `lockedCurrencyAmount`. For example, if I want to send exactly $5 MXN from my wallet, I would set this to "sending", and the `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to receive exactly $10 USD, I would set this to "receiving" and the `lockedCurrencyAmount` to 10000 (in cents). - QuoteRequest: - type: object - required: - - source - - destination - - lockedCurrencySide - - lockedCurrencyAmount - properties: - lookupId: - type: string - description: |- - Lookup ID from a previous receiver lookup request. If provided, this can make the quote creation more efficient by reusing cached lookup data. - NOTE: This is required for UMA destinations due to counterparty institution requirements. See `senderCustomerInfo` for more information. - example: Lookup:019542f5-b3e7-1d02-0000-000000000009 - source: - $ref: '#/components/schemas/QuoteSourceOneOf' - destination: - $ref: '#/components/schemas/QuoteDestinationOneOf' - lockedCurrencySide: - $ref: '#/components/schemas/QuoteLockSide' - lockedCurrencyAmount: - type: integer - format: int64 - description: The amount to send/receive in the smallest unit of the locked currency (eg. cents). See `lockedCurrencySide` for more information. - exclusiveMinimum: 0 - maximum: 9000000000000000 - example: 1000 - immediatelyExecute: - type: boolean - description: Whether to immediately execute the quote after creation. If true, the quote will be executed and the transaction will be created at the current exchange rate. It should only be used if you don't want to lock and view rate details before executing the quote. If you are executing a pre-existing quote, use the `/quotes/{quoteId}/execute` endpoint instead. This is false by default. - example: false - description: - type: string - description: Optional description/memo for the transfer - example: 'Invoice #1234 payment' - senderCustomerInfo: - type: object - additionalProperties: true - description: | - Only relevant for UMA destinations. - Key-value pairs of information about the sender which was requested by the counterparty (recipient) institution. - Any fields specified in `requiredPayerDataFields` from the response of the `/receiver/uma/{receiverUmaAddress}` (lookupUma) endpoint - MUST be provided here if they were requested. If the counterparty (recipient) institution did not request any information, this field can be omitted. - example: - FULL_NAME: Jane Receiver - NATIONALITY: FR - TestWebhookResponse: - type: object - required: - - response_status - properties: - url: - type: string - format: uri - description: URL where the webhook was sent - example: https://api.mycompany.com/webhooks/uma - response_status: - type: integer - description: The HTTP status code returned by the webhook endpoint - example: 200 - response_body: - type: string - description: The raw body content returned by the webhook endpoint in response to the request - GridError: - type: object - title: GridError - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - BulkCustomerImportErrorEntry: - allOf: - - $ref: '#/components/schemas/GridError' - - type: object - description: Error information for a failed bulk import entry - required: - - correlationId - properties: - correlationId: - type: string - description: Platform customer ID or row number for the failed entry - example: biz456 - BulkCustomerImportJob: - type: object - required: - - jobId - - status - - progress - properties: - jobId: - type: string - description: Unique identifier for the bulk import job - example: Job:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - description: Current status of the job - example: PROCESSING - progress: - type: object - required: - - total - - processed - - successful - - failed - properties: - total: - type: integer - description: Total number of customers to process - example: 5000 - processed: - type: integer - description: Number of customers processed so far - example: 2500 - successful: - type: integer - description: Number of customers successfully created - example: 2450 - failed: - type: integer - description: Number of customers that failed to create - example: 50 - errors: - type: array - description: Detailed error information for failed entries - items: - $ref: '#/components/schemas/BulkCustomerImportErrorEntry' - completedAt: - type: string - format: date-time - description: Timestamp when the job completed (only present for COMPLETED or FAILED status) - example: '2025-08-15T14:32:00Z' - UmaInvitation: - type: object - required: - - code - - createdAt - - inviterUma - - status - - url - properties: - code: - type: string - description: The unique code of the invitation - example: 019542f5 - createdAt: - type: string - format: date-time - description: When the invitation was created - example: '2025-09-01T14:30:00Z' - claimedAt: - type: string - format: date-time - description: When the invitation was claimed if it has been claimed - example: '2025-09-01T14:30:00Z' - url: - type: string - description: The URL where this invitation can be claimed. - example: https://uma.me/i/019542f5 - expiresAt: - type: string - format: date-time - description: When the invitation expires (if at all) - example: '2025-09-01T14:30:00Z' - inviterUma: - type: string - description: The UMA address of the inviter - example: $inviter@uma.domain - inviteeUma: - type: string - description: The UMA address of the invitee - example: $invitee@uma.domain - status: - type: string - enum: - - PENDING - - CLAIMED - - EXPIRED - - CANCELLED - description: The status of the invitation - example: PENDING - firstName: - type: string - description: The inviter's first name. Will be displayed when the recipient clicks the invite link - example: Jane - amountToSend: - $ref: '#/components/schemas/CurrencyAmount' - description: |- - The amount to send to the invitee when the invitation is claimed. This is optional and if not provided, the invitee will not receive any amount. Note that the actual sending of the amount must be done by the inviter platform once the INVITATION_CLAIMED webhook is received. If the inviter platform either does not send the payment or the payment fails, the invitee will not receive this amount. This field is primarily used for display purposes on the claiming side of the invitation. - This field is useful for "send-by-link" style customer flows where an inviter can send a payment simply by sharing a link without knowing the receiver's UMA address. Note that these sends can only be sender-locked, meaning that the sender will not know ahead of time how much the receiver will receive in the receiving currency. - Error403: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 403 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | FORBIDDEN | Insufficient permissions | - | USER_NOT_READY | Customer exists but is not ready for operation | - | COUNTERPARTY_NOT_ALLOWED | Counterparty has not been enabled for your account | - | VELOCITY_LIMIT_EXCEEDED | Counterparty has exceeded velocity limits | - enum: - - FORBIDDEN - - USER_NOT_READY - - COUNTERPARTY_NOT_ALLOWED - - VELOCITY_LIMIT_EXCEEDED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - UmaProvider: - type: object - properties: - name: - type: string - description: Name of the UMA Provider - example: Lightspark Group - supportedRegions: - type: array - items: - type: string - description: Region(s) this UMA Provider operates in - example: - - US - domain: - type: string - description: Domain this VASP uses for UMA addresses - example: uma.me - logoUrl: - type: string - description: Logo URL for the VASP - format: uri - example: https://uma.me/logo.png - supportedCurrencies: - type: array - items: - $ref: '#/components/schemas/Currency' - description: List of currencies supported by this UMA Provider - example: - - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - lei: - type: string - description: Legal Entity Identifier for the UMA Provider - example: 5493001KJTIIGC8Y1R12 - allowListStatus: - type: boolean - description: Whether this UMA Provider is on your allow list - example: true - Permission: - type: string - enum: - - VIEW - - TRANSACT - - MANAGE - description: 'Permission of an API token that determines what actions the token can perform: VIEW: Can view all data, including platform config, customers and transactions TRANSACT: Can send payments MANAGE: Can manage platform config, api tokens and customers' - ApiToken: - type: object - required: - - id - - name - - permissions - - clientId - - createdAt - - updatedAt - properties: - id: - type: string - description: System-generated unique identifier - example: Token:019542f5-b3e7-1d02-0000-000000000001 - name: - type: string - description: Name of the token - example: Sandbox read-only token - permissions: - type: array - description: A list of permissions granted to the token - items: - $ref: '#/components/schemas/Permission' - clientId: - type: string - description: An opaque identifier that should be used as a client_id (or username) in the HTTP Basic Authentication scheme when issuing http requests to Grid. - example: 01947d2284054f890000e63bca4810df - clientSecret: - type: string - description: The secret that should be used to authenticate against Grid API. This secret is not stored and will never be available again after creation. Platform must keep this secret secure as it grants access to the account. - example: ed0ad25881e234cc28fb2dec0a4fe64e4172 - createdAt: - type: string - format: date-time - description: Creation timestamp - example: '2025-07-21T17:32:28Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - example: '2025-07-21T17:32:28Z' - IncomingPaymentWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - transaction - properties: - transaction: - $ref: '#/components/schemas/IncomingTransaction' - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: INCOMING_PAYMENT - requestedReceiverCustomerInfoFields: - type: array - items: - $ref: '#/components/schemas/CounterpartyFieldDefinition' - description: Information required by the sender's VASP about the recipient. Platform must provide these in the 200 OK response if approving. Note that this only includes fields which Grid does not already have from initial customer registration. - BaseWebhook: - type: object - required: - - timestamp - - webhookId - - type - properties: - timestamp: - type: string - format: date-time - description: ISO8601 timestamp when the webhook was sent (can be used to prevent replay attacks) - example: '2025-08-15T14:32:00Z' - webhookId: - type: string - description: Unique identifier for this webhook delivery (can be used for idempotency) - example: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - discriminator: - propertyName: type - mapping: - INCOMING_PAYMENT: '#/components/schemas/IncomingPaymentWebhook' - OUTGOING_PAYMENT: '#/components/schemas/OutgoingPaymentWebhook' - TEST: '#/components/schemas/TestWebhookRequest' - BULK_UPLOAD: '#/components/schemas/BulkUploadWebhookRequest' - INVITATION_CLAIMED: '#/components/schemas/InvitationClaimedWebhook' - KYC_STATUS: '#/components/schemas/KycStatusWebhook' - WebhookType: - type: string - enum: - - INCOMING_PAYMENT - - OUTGOING_PAYMENT - - TEST - - BULK_UPLOAD - - INVITATION_CLAIMED - - KYC_STATUS - - ACCOUNT_STATUS - description: Type of webhook event, used by the receiver to identify which webhook is being received - OutgoingPaymentWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - transaction - properties: - transaction: - $ref: '#/components/schemas/OutgoingTransaction' - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: OUTGOING_PAYMENT - TestWebhookRequest: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - properties: - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: TEST - BulkUploadWebhookRequest: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - bulkCustomerImportJob - properties: - bulkCustomerImportJob: - $ref: '#/components/schemas/BulkCustomerImportJob' - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: BULK_UPLOAD - InvitationClaimedWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - invitation - properties: - invitation: - $ref: '#/components/schemas/UmaInvitation' - type: - type: string - enum: - - INVITATION_CLAIMED - description: Type of webhook event - example: INVITATION_CLAIMED - KycStatusWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - customerId - - kycStatus - properties: - customerId: - type: string - description: System generated id of the customer - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - kycStatus: - $ref: '#/components/schemas/KycStatus' - IncomingPaymentWebhookResponse: - type: object - properties: - receiverCustomerInfo: - type: object - additionalProperties: true - description: Information about the recipient, provided by the platform if requested in the webhook via `requestedReceiverCustomerInfoFields` and the payment is approved. - IncomingPaymentWebhookForbiddenResponse: - allOf: - - $ref: '#/components/schemas/GridError' - - type: object - properties: - reason: - type: string - description: Optional reason for rejecting the payment. This is just for debugging purposes or can be used for a platform's own purposes. - example: RESTRICTED_JURISDICTION - IncomingPaymentWebhookUnprocessableResponse: - allOf: - - $ref: '#/components/schemas/GridError' - - type: object - properties: - requiredFields: - type: array - items: - type: string - description: List of fields that are required by the platform, but are not present in the counterparty information. - example: - - TAX_ID - - REGISTRATION_NUMBER - AccountStatusWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - accountId - properties: - accountId: - type: string - description: The id of the account whose balance has changed - oldBalance: - $ref: '#/components/schemas/CurrencyAmount' - newBalance: - $ref: '#/components/schemas/CurrencyAmount' - customerId: - type: string - description: The ID of the customer associated with the internal account - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: The ID of the customer as associated in your platform - example: 019542f5-b3e7-1d02-0000-000000000001 diff --git a/mintlify/payouts-and-b2b/depositing-funds/depositing-funds.mdx b/mintlify/payouts-and-b2b/depositing-funds/depositing-funds.mdx deleted file mode 100644 index d7fde2ac..00000000 --- a/mintlify/payouts-and-b2b/depositing-funds/depositing-funds.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Depositing Funds" -description: "Depositing funds into internal accounts" ---- - -import DepositingFunds from '/snippets/depositing-funds.mdx'; - - diff --git a/mintlify/payouts-and-b2b/depositing-funds/external-accounts.mdx b/mintlify/payouts-and-b2b/depositing-funds/external-accounts.mdx deleted file mode 100644 index d5736b41..00000000 --- a/mintlify/payouts-and-b2b/depositing-funds/external-accounts.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "External Accounts" -description: "Add and manage external bank accounts, wallets, and payment destinations for withdrawals and payouts" ---- - -import ExternalAccounts from '/snippets/external-accounts.mdx'; - - - -## Next steps - - - - Simplify external account setup with Plaid Link for instant bank verification - - - - Learn how to send international payments using external accounts - - - - View complete API documentation for external accounts - - diff --git a/mintlify/payouts-and-b2b/depositing-funds/internal-accounts.mdx b/mintlify/payouts-and-b2b/depositing-funds/internal-accounts.mdx deleted file mode 100644 index 8902b682..00000000 --- a/mintlify/payouts-and-b2b/depositing-funds/internal-accounts.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "Internal Accounts" -description: "Learn how to manage internal accounts for holding platform and customer funds" ---- - -import InternalAccounts from '/snippets/internal-accounts.mdx'; - - - -## Next steps - - - -Learn how to add customer bank accounts as withdrawal destinations - - - -Simplify bank account verification with Plaid Link - - - -Use internal account balances to send international payments - - - -View complete API documentation for internal accounts - - diff --git a/mintlify/payouts-and-b2b/depositing-funds/plaid.mdx b/mintlify/payouts-and-b2b/depositing-funds/plaid.mdx deleted file mode 100644 index 65fe4f78..00000000 --- a/mintlify/payouts-and-b2b/depositing-funds/plaid.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "External Accounts with Plaid" -description: "Simplify bank account verification with Plaid Link for external account setup" ---- - -import PlaidIntegration from '/snippets/plaid-integration.mdx'; - - - -## Next steps - - - -Learn more about managing external accounts - - - - Transfer funds between internal and external accounts - - - - Set up webhook handling for account notifications - - - -View complete Plaid API documentation - - diff --git a/mintlify/payouts-and-b2b/index.mdx b/mintlify/payouts-and-b2b/index.mdx deleted file mode 100644 index c57ff2e2..00000000 --- a/mintlify/payouts-and-b2b/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: "Introduction" -description: "Integrate Global payments quickly and easily" ---- - -import { payToBankProductName, topLevelProductName } from '/snippets/variables.mdx'; -import PaymentFlow from '/snippets/payment-flow.mdx'; -import CountrySupport from '/snippets/country-support.mdx'; - -With {payToBankProductName}, you can send and receive low cost real-time payments to bank accounts worldwide through a single, simple API. {topLevelProductName} automatically routes each payment across its network of {topLevelProductName} switches, handling FX, blockchain settlement, and instant banking off-ramps for you. - - - - - Single API, global reach. {payToBankProductName} interacts with the Money Grid to route your payments globally. - - - No crypto handling. {payToBankProductName} converts between fiat and crypto instantly to simplify your implementation and minimize FX costs. - - - Real-time settlement. Leverages local instant banking rails and global low latency crypto rails to settle payments in real-time. - - - - - -## {payToBankProductName} Features -Customers interact with {payToBankProductName} through two main interfaces. - - - Programmatic access to create customers, quotes, fund the account, send payments and reconcile via webhooks. - - - Your development and operations team can use the dashboard to monitor payments and webhooks, manage API keys and environments, and troubleshoot with logs. - - - -Implementing cross-border payments with {payToBankProductName} is simple. Here's a quick overview of the main steps. - -### Onboarding customers - - -{payToBankProductName} has two customer onboarding options - one for non regulated entities where {payToBankProductName} handles the KYC/KYB process and one for regulated entities where you handle the KYC/KYB process. - -When creating customers, you'll be able to also connect external accounts or internal accounts to a specific customer and also add accounts for counterparties on the receiving end. -To learn more about accounts read our [internal accounts guide](/payouts-and-b2b/depositing-funds/internal-accounts) or setting up [external accounts guide](/payouts-and-b2b/depositing-funds/external-accounts) - - -### Funding Payments -{payToBankProductName} supports multiple transaction funding options including prefunded accounts and real-time funding. You can prefund an account using several payment rails such as ACH, SEPA Instant, wire transfers, Lightning, and more. - -With real-time funding, you'll receive payment instructions as part of the quote. Once payment is received by our services, we'll initiate the payment to the receiver. - -### Sending Payments -To send with {payToBankProductName}, onboard an account for a customer and the counter party, then execute and fund a quote. {payToBankProductName} resolves the receiver by external bank details, returns min/max and an exchange rate, and provides funding instructions. Once funded, {payToBankProductName} handles FX and delivery to the receiving account. - -### Environments - - -{payToBankProductName} supports two environments: Sandbox and Production. Sandbox mirrors production behavior and webhooks so you can test receiver resolution, quotes and funding instructions, settlement status changes, and full end‑to‑end flows without moving real funds. Production uses live credentials and base URLs for real payments once you’re ready to launch. - - ---- - -### Country Availability - - diff --git a/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx b/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx deleted file mode 100644 index d3bf13b5..00000000 --- a/mintlify/payouts-and-b2b/onboarding/configuring-customers.mdx +++ /dev/null @@ -1,345 +0,0 @@ ---- -title: "Configuring Customers" -description: "Configuring customers for Payouts" ---- - -import KycRegulated from '/snippets/kyc/kyc-regulated.mdx' -import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx' - -This guide provides comprehensive information about customer configuration in the Grid API, including customer types, registration processes, management, and bank account information. - -## Customer Types - -The Grid API supports both individual and business customers. While the API schema itself makes most Personally Identifiable Information (PII) optional at the initial customer creation, specific fields may become mandatory based on the currencies the customer will transact with. - -Your platform's configuration (retrieved via `GET /config`) includes a `supportedCurrencies` array. If a customer is intended to use a specific currency, any fields listed in for that currency **must** be provided when creating or updating the customer. - - -## Customer Registration Process - -### Creating a New Customer - -When creating or updating customers, the `customerType` field must be specified as either `INDIVIDUAL` or `BUSINESS`. Depending if you are a regulated or unregulated platform your KYC/KYB requirements will vary. - - - - - - - - - - - -## Customer Management - -### Retrieving Customer Information - -You can retrieve customer information using either the Grid-assigned customer ID or your platform's customer ID: - - -```bash Grid-assigned customer ID -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -```bash List customers with a filter -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers?platformCustomerId={platformCustomerId}&customerType={customerType}&createdAfter={createdAfter}&createdBefore={createdBefore}&cursor={cursor}&limit={limit}" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - -Note that this example shows all available filters. You can use any combination of them. - -### Updating Customer Information - -To update customer information: - -```bash -curl -X PATCH "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "bankAccountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "987654321", - "bankName": "Chase Bank" - } - }' -``` - -Note that not all customer information can be updated. Particularly for non-regulated platforms, you cannot update personal information after the customer has been created. - -## Bank Account Information - -The API supports various bank account formats based on country and funding type. There are two types of funding -mechanisms supported by Grid: an omnibus FBO (for benefit of) account owned by the platform, or direct customer-owned accounts. You must provide the correct format based on the customer's region and bank account type. - -### Optional Platform Account ID - -All bank account types support an optional `platformAccountId` field that allows you to link bank accounts to your internal systems. This field can be any string that helps identify the account in your platform (e.g., database IDs, custom references, etc.). - -Example with platform account ID: - -```json -{ - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "987654321", - "bankName": "Chase Bank", - "platformAccountId": "chase_primary_1234" -} -``` - -Common use cases for `platformAccountId`: - -- Tracking multiple bank accounts and uma addresses for the same customer -- Linking accounts to internal accounting systems -- Maintaining consistency between the Grid API and your platform's account records -- Facilitating account reconciliation and reporting - - - - FBO accounts are used when the platform has a single omnibus account that is used to fund all customers. Account details - must be provided manually at the platform level. For each customer, during you should simply provide: - - ```json - "bankAccountInfo": { - "accountType": "FBO", - "currencyCode": "USD" // or any other currency code supported by the Grid API - } - ``` - - - Please contact us to set up FBO account for a specific currency. - - - - ```json - { - "accountType": "CLABE", - "clabeNumber": "123456789012345678", - "bankName": "Banco de México", - "platformAccountId": "banco_mx_primary_5678" - } - ``` - - - ```json - { - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "987654321", - "accountCategory": "CHECKING", - "bankName": "Chase Bank", - "platformAccountId": "chase_checking_1234" - } - ``` - - - - ```json - { - "accountType": "PIX", - "pixKey": "12345678901", - "pixKeyType": "CPF", - "platformAccountId": "pix_main_9012" - } - ``` - - PIX key types can be one of: `CPF`, `CNPJ`, `PHONE`, `EMAIL`, or `RANDOM`. - - - - ```json - { - "accountType": "UPI", - "vpa": "somecustomer@okbank", - "platformAccountId": "upi_primary_1234" - } - ``` - - - - ```json - { - "accountType": "IBAN", - "iban": "DE89370400440532013000", - "bankName": "Deutsche Bank", - "platformAccountId": "deutsche_primary_3456" - } - ``` - - - -## Data Validation - -The Grid API performs validation on all customer data. Common validation rules include: - -- All required fields must be present based on customer type -- Date of birth must be in YYYY-MM-DD format and represent a valid date -- Names must not contain special characters or excessive spaces -- Bank account information must follow country-specific formats -- Addresses must include all required fields including country code - -If validation fails, the API will return a 400 Bad Request response with detailed error information. - -## Best Practices - -1. **Identity Verification**: Choose a proper KYC/KYB identity verification flow as detailed in the [Quickstart Guide](/payouts-and-b2b/quickstart#choosing-your-onboarding-flow) -2. **Data Security**: Store and transmit customer data securely, following data protection regulations -3. **Regular Updates**: Keep customer information up to date, especially banking details -4. **Error Handling**: Implement proper error handling to manage validation failures gracefully -5. **Idempotent Operations**: Use your platformCustomerId consistently to avoid duplicate customer creation - -## Bulk Customer Import Operations - -For scenarios where you need to add many customers to the system at once, the API provides a CSV file upload endpoint. - -### CSV File Upload - -For large-scale customer imports, you can upload a CSV file containing customer information: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers/bulk/csv" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -F "file=@customers.csv" -``` - -The CSV file should follow a specific format with required and optional columns based on customer type. Here's an example: - -```csv -platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,accountType,accountNumber,bankName,platformAccountId,businessLegalName,routingNumber,accountCategory -customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US,US_ACCOUNT,123456789,Chase Bank,chase_primary_1234,,222888888,SAVINGS -biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US,US_ACCOUNT,987654321,Bank of America,boa_business_5678,Acme Corp,121212121,CHECKING -``` - - -CSV Upload Best Practices - -1. Use a spreadsheet application to prepare your CSV file -2. Validate data before upload (e.g., date formats, required fields) -3. Include a header row with column names -4. Use UTF-8 encoding for special characters -5. Keep file size under 100MB for optimal processing - - -You can track the job status through: - -1. Webhook notifications (if configured) -2. Status polling endpoint: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers/bulk/jobs/{jobId}" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Example job status response: - -```json -{ - "jobId": "job_123456789", - "status": "PROCESSING", - "progress": { - "total": 5000, - "processed": 2500, - "successful": 2499, - "failed": 1 - }, - "errors": [ - { - "platformCustomerId": "biz456", - "error": { - "code": "validation_error", - "message": "Invalid bank account number" - } - } - ] -} -``` - - -Best Practices for Bulk Operations - -1. Use platform customer IDs to track individual customers in the bulk operation -2. Implement proper error handling for partial successes -3. Consider breaking very large datasets into multiple smaller jobs -4. Use webhooks for real-time status updates on asynchronous jobs -5. For CSV uploads, validate your data before submission - - -### CSV Format - -The CSV file should have the following columns: - -Required columns for all customers: - -- platformCustomerId: Your platform's unique identifier for the customer -- customerType: Either "INDIVIDUAL" or "BUSINESS" - -Required columns for individual customers: - -- fullName: Individual's full name -- birthDate: Date of birth in YYYY-MM-DD format -- addressLine1: Street address line 1 -- city: City -- state: State/Province/Region -- postalCode: Postal/ZIP code -- country: Country code (ISO 3166-1 alpha-2) -- accountType: Bank account type (CLABE, US_ACCOUNT, PIX, IBAN, UPI) -- accountNumber: Bank account number -- bankName: Name of the bank - -Required columns for business customers: - -- businessLegalName: Legal name of the business -- addressLine1: Street address line 1 -- city: City -- state: State/Province/Region -- postalCode: Postal/ZIP code -- country: Country code (ISO 3166-1 alpha-2) -- accountType: Bank account type (CLABE, US_ACCOUNT, PIX, IBAN, UPI) -- accountNumber: Bank account number -- bankName: Name of the bank - -Optional columns for all customers: - -- addressLine2: Street address line 2 -- platformAccountId: Your platform's identifier for the bank account -- description: Optional description for the customer - -Optional columns for individual customers: - -- email: Customer's email address - -Optional columns for business customers: - -- businessRegistrationNumber: Business registration number -- businessTaxId: Tax identification number - -Additional required columns based on account type: - -For US_ACCOUNT: - -- routingNumber: ACH routing number (9 digits) -- accountCategory: Either "CHECKING" or "SAVINGS" - -For CLABE: - -- clabeNumber: 18-digit CLABE number - -For PIX: - -- pixKey: PIX key value -- pixKeyType: Type of PIX key (CPF, CNPJ, EMAIL, PHONE, RANDOM) - -For UPI: - -- vpa: Virtual Payment Address for UPI payments - -For IBAN: - -- iban: International Bank Account Number -- swiftBic: SWIFT/BIC code (8 or 11 characters) - diff --git a/mintlify/payouts-and-b2b/onboarding/implementation-overview.mdx b/mintlify/payouts-and-b2b/onboarding/implementation-overview.mdx deleted file mode 100644 index 46a72703..00000000 --- a/mintlify/payouts-and-b2b/onboarding/implementation-overview.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "Implementation Overview" ---- -This page gives you a 10,000‑ft view of an end‑to‑end implementation. It is intentionally generalized because the flow supports multiple customer types and external account types (e.g., CLABE, IBAN, US accounts, UPI). The detailed guides that follow provide concrete fields, edge cases, and step‑by‑step instructions. - - -This overview highlights the main building blocks: platform setup, onboarding, funding, payout accounts, sending and receiving flows, reconciliation, sandbox testing, and go‑live enablement. - - -## Platform configuration -Configure your platform once before building customer flows. - -- Provide webhook endpoints for outgoing and incoming payment notifications -- Generate API credentials for Sandbox (and later Production) -- Review regional capabilities (rails, currencies, settlement windows) - -## Onboarding customers -Onboard customers and accounts. There are two patterns: - -- Regulated entities can directly create customers by providing KYC/KYB data via API -- Unregulated entities should request a KYC link and embed the hosted KYC flow; once completed, the customer can transact. - -You'll also need to persist the Grid customer IDs for use in payment flows - - -## Account funding -Choose how transactions are funded based on your product design and region. - -- Prefunded: Maintain balances in one or more currencies/cryptocurrencies and spend from those balances -- Just‑in‑time (JIT): Create a quote and fund it in real time using the payment instructions provided; ideal when you don’t wish to hold float - - -You can mix models as necessary. But it may make reconciliation more complex. - - -## External account creation -Register accounts your customers will send to or receive from, such as CLABE (MX), IBAN (EU/UK), ACH/RTP(US), UPI (IN), Spark address, and others. -- Capture beneficiary details (individual or business) and required banking fields -- Validate account formats where applicable and map them to your internal customer - -## Sending payments -Sending consists of lookup, pricing, funding, and execution. -- Resolve the counterparty: look up receiver account for compliance review and to determine capabilities -- Create a quote: specify source/destination, currencies, and whether you lock sending or receiving amount; receive exchange rate, limits, fees, and (for JIT) funding instructions -- Fund and execute: for prefunded, confirm/execute; for JIT, push funds exactly as instructed (amount, reference) and the platform handles FX and delivery -- Observe status via webhooks and surface outcomes in your UI - -## Receiving payments -Enable customers to receive funds to their linked bank account. -- Expose customer addressing to payers -- The platform handles conversion and offramping to the receiver’s account currency -- Approve or auto‑approve per your policy; update balances on completion via webhooks - -## Reconciling transactions -Implement operational processes to keep your ledger in sync. -- Process webhooks idempotently; map statuses (pending, processing, completed, failed) -- Tie transactions back to quotes and customers; persist references -- Query for transactions by date range or other filters as necessary - -## Testing in Sandbox -Use Sandbox to build and validate end‑to‑end without moving real funds. -- Exercise receiver lookup, quote creation, funding instructions, and webhook lifecycles -- Validate compliance decisioning with realistic but synthetic data -- Optionally use the Test Wallet as a counterparty for faster iteration (see Tools) - -## Enabling Production -When you’re ready to go live: -- Complete corridor and provider onboarding as needed for your regions -- Confirm webhook security, monitoring, and alerting are in place -- Review rate limits, error handling, retries, and idempotency keys -- Run final UAT in Sandbox, then request Production access from our team - - -Contact our team to enable Production and finalize corridor activations. - - diff --git a/mintlify/payouts-and-b2b/onboarding/platform-configuration.mdx b/mintlify/payouts-and-b2b/onboarding/platform-configuration.mdx deleted file mode 100644 index e9a631aa..00000000 --- a/mintlify/payouts-and-b2b/onboarding/platform-configuration.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Platform Configuration" -description: "Configuring credentials, webhooks and currencies for your platform" ---- -import PlatformConfigAPI from '/snippets/platform-config-currency-api-webhooks.mdx'; - - \ No newline at end of file diff --git a/mintlify/payouts-and-b2b/payment-flow/error-handling.mdx b/mintlify/payouts-and-b2b/payment-flow/error-handling.mdx deleted file mode 100644 index 18346667..00000000 --- a/mintlify/payouts-and-b2b/payment-flow/error-handling.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Error Handling" -description: "Handle payment failures, API errors, and transaction issues gracefully" ---- - -import ErrorHandling from '/snippets/error-handling.mdx' - - diff --git a/mintlify/payouts-and-b2b/payment-flow/list-transactions.mdx b/mintlify/payouts-and-b2b/payment-flow/list-transactions.mdx deleted file mode 100644 index 8884dbb7..00000000 --- a/mintlify/payouts-and-b2b/payment-flow/list-transactions.mdx +++ /dev/null @@ -1,361 +0,0 @@ ---- -title: "List Transactions" -description: "Query and filter payment history with powerful filtering and pagination options" ---- - -Retrieve transaction history with flexible filtering options. Transactions are returned in descending order (most recent first) with a default limit of 20 per page. - - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' - -{ - "data": [ - { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "COMPLETED", - "type": "OUTGOING", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "sentAmount": { - "amount": 10000, - "currency": { - "code": "USD", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 9200, - "currency": { - "code": "EUR", - "symbol": "€", - "decimals": 2 - } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "customer_12345", - "description": "Payment for services - Invoice #1234", - "exchangeRate": 0.92, - "settledAt": "2025-10-03T15:30:00Z", - "createdAt": "2025-10-03T15:00:00Z" - } - ], - "hasMore": true, - "nextCursor": "eyJpZCI6IlRyYW5zYWN0aW9uOjAxOTU0MmY1LWIzZTctMWQwMi0wMDAwLTAwMDAwMDAwMDAzMCJ9", - "totalCount": 45 -} -``` - -## Filtering transactions - -Use query parameters to filter and narrow down transaction results. - -### Filter by customer - -Get all transactions for a specific customer: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Or use your platform's customer ID: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?platformCustomerId=customer_12345' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -### Filter by transaction type - -Get only incoming or outgoing transactions: - - - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?type=OUTGOING' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - - - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?type=INCOMING' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - - - -### Filter by status - -Get transactions in a specific status: - -```bash -# Get all pending transactions -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?status=PENDING' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' - -# Get all completed transactions -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?status=COMPLETED' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -**Available statuses:** - -- `PENDING` - Transaction initiated, awaiting processing -- `PROCESSING` - Transaction in progress -- `COMPLETED` - Transaction successfully completed -- `FAILED` - Transaction failed -- `REJECTED` - Transaction rejected - -### Filter by date range - -Get transactions within a specific date range: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?startDate=2025-10-01T00:00:00Z&endDate=2025-10-31T23:59:59Z' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Dates must be in ISO 8601 format (e.g., `2025-10-03T15:00:00Z`). - -### Filter by account - -Get transactions for a specific account: - -```bash -# Any transactions involving this account (sent or received) -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?accountIdentifier=InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' - -# Only transactions sent FROM this account -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?senderAccountIdentifier=InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' - -# Only transactions sent TO this account -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?receiverAccountIdentifier=ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Combining filters - -You can combine multiple filters to narrow down results: - -```bash -# Get completed outgoing transactions for a customer in October 2025 -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001&type=OUTGOING&status=COMPLETED&startDate=2025-10-01T00:00:00Z&endDate=2025-10-31T23:59:59Z' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Pagination - -Handle large result sets with cursor-based pagination: - - - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?limit=20' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Response: - -```json -{ - "data": [ - /* 20 transactions */ - ], - "hasMore": true, - "nextCursor": "eyJpZCI6IlRyYW5z...", - "totalCount": 150 -} -``` - - - - -Use the `nextCursor` from the previous response: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?limit=20&cursor=eyJpZCI6IlRyYW5z...' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - - - - -Keep paginating while `hasMore` is `true`: - -```javascript -async function getAllTransactions() { - const allTransactions = []; - let cursor = null; - let hasMore = true; - - while (hasMore) { - const url = cursor - ? `https://api.lightspark.com/grid/2025-10-13/transactions?limit=100&cursor=${cursor}` - : `https://api.lightspark.com/grid/2025-10-13/transactions?limit=100`; - - const response = await fetch(url, { - headers: { Authorization: `Basic ${credentials}` }, - }); - - const { data, hasMore: more, nextCursor } = await response.json(); - - allTransactions.push(...data); - hasMore = more; - cursor = nextCursor; - } - - return allTransactions; -} -``` - - - - -The maximum `limit` is 100 transactions per request. Default is 20. - -## Get a single transaction - -Retrieve details for a specific transaction by ID: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000030' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Best practices - - - -Always implement pagination when fetching transactions to avoid timeouts and memory issues: - -```javascript -// Good: Paginated approach -async function fetchWithPagination(filters) { - const transactions = []; - let cursor = null; - - do { - const page = await fetchTransactionPage(filters, cursor); - transactions.push(...page.data); - cursor = page.nextCursor; - } while (cursor); - - return transactions; -} - -// Bad: Trying to fetch everything at once -async function fetchAll() { - const response = await fetch("/transactions?limit=10000"); // Don't do this! - return response.json(); -} -``` - - - - -For data that doesn't change frequently (like completed transactions), implement caching: - -```javascript -const cache = new Map(); - -async function getCompletedTransactions(customerId, month) { - const cacheKey = `${customerId}-${month}`; - - if (cache.has(cacheKey)) { - return cache.get(cacheKey); - } - - const transactions = await fetchMonthlyTransactions(customerId, month); - cache.set(cacheKey, transactions); - - return transactions; -} -``` - - - - -Apply filters to get only the data you need: - -```javascript -// Good: Specific filters -const recentFailed = await fetch( - "/transactions?status=FAILED&startDate=2025-10-01T00:00:00Z&type=OUTGOING" -); - -// Bad: Fetch everything and filter in code -const all = await fetch("/transactions"); -const filtered = all.data.filter( - (tx) => - tx.status === "FAILED" && - tx.type === "OUTGOING" && - new Date(tx.createdAt) > new Date("2025-10-01") -); -``` - - - - -Implement exponential backoff for rate limit errors: - -```javascript -async function fetchWithRetry(url, retries = 3) { - for (let i = 0; i < retries; i++) { - const response = await fetch(url, { - headers: { Authorization: `Basic ${credentials}` }, - }); - - if (response.status === 429) { - const retryAfter = parseInt( - response.headers.get("Retry-After") || "1", - 10 - ); - await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000)); - continue; - } - - return response.json(); - } - - throw new Error("Max retries exceeded"); -} -``` - - - - -## Next steps - - - - Learn how to send payments from internal accounts - - - - Match payments with your internal accounting systems - - - - Handle transaction failures and errors - - diff --git a/mintlify/payouts-and-b2b/payment-flow/reconciliation.mdx b/mintlify/payouts-and-b2b/payment-flow/reconciliation.mdx deleted file mode 100644 index 64ceeb89..00000000 --- a/mintlify/payouts-and-b2b/payment-flow/reconciliation.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Reconciliation" -description: "Match Grid transactions with your internal systems" ---- -import Reconciliation from '/snippets/reconciliation/reconciliation.mdx' - - diff --git a/mintlify/payouts-and-b2b/payment-flow/send-payment.mdx b/mintlify/payouts-and-b2b/payment-flow/send-payment.mdx deleted file mode 100644 index afad8016..00000000 --- a/mintlify/payouts-and-b2b/payment-flow/send-payment.mdx +++ /dev/null @@ -1,582 +0,0 @@ ---- -title: "Sending Payments" -description: "Learn how to send payments from internal accounts to external bank accounts with same-currency and cross-currency transfers" ---- - -Send payments from your customers' internal accounts to their external bank accounts or to other destinations. Grid supports both same-currency transfers and cross-currency transfers with automatic exchange rate handling. - -## Overview - -Grid provides two payment methods depending on your use case: - - - - Send funds in the same currency from an internal account to an external account. Fast and straightforward. - - - - Send funds with currency conversion using real-time exchange rates. Supports multiple fiat currencies and payment rails. - - - -## Prerequisites - -Before sending payments, ensure you have: - -- An active internal account with sufficient balance -- A verified external account for the destination -- Valid API credentials with appropriate permissions -- A webhook endpoint configured to receive payment status updates (recommended) - - - If you don't have these set up yet, review the [Internal - Accounts](/payouts-and-b2b/depositing-funds/internal-accounts) and [External - Accounts](/payouts-and-b2b/depositing-funds/external-accounts) guides first. - - -## Same-Currency Transfers - -Use the `/transfer-out` endpoint when sending funds in the same currency (no exchange rate needed). This is the simplest and fastest option for domestic transfers. - -### When to use same-currency transfers - -- Transferring USD from a USD internal account to a USD external account -- Sending funds within the same country using the same payment rail -- No currency conversion is required - -### Create a transfer - - - - Retrieve the internal account (source) and external account (destination) IDs: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Note the `id` fields from both the internal and external accounts you want to use. - - - - - Create the transfer by specifying the source and destination accounts: - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/transfer-out' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "accountId": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123" - }, - "destination": { - "accountId": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "amount": 12550 - }' -``` - - -```json Success (201 Created) -{ - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000015", - "status": "PENDING", - "type": "OUTGOING", - "source": { - "accountId": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "sentAmount": { - "amount": 12550, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 12550, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "customer_12345", - "createdAt": "2025-10-03T15:00:00Z", - "settledAt": null -} -``` - - - The `amount` is specified in the smallest unit of the currency (cents for USD, pence for GBP, etc.). For example, `12550` represents $125.50 USD. - - - - - The transaction is created with a `PENDING` status. Monitor the status by: - -**Option 1: Webhook notifications** (recommended) - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000015", - "status": "COMPLETED", - "settledAt": "2025-10-03T15:02:30Z" - }, - "timestamp": "2025-10-03T15:03:00Z" -} -``` - -**Option 2: Poll the transaction endpoint** - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000015' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - - - When the transaction status changes to `COMPLETED`, the funds have been successfully transferred to the external account. - - - - -### Transaction statuses - -| Status | Description | -| ------------ | --------------------------------------------- | -| `PENDING` | Transfer initiated and awaiting processing | -| `PROCESSING` | Transfer in progress through the payment rail | -| `COMPLETED` | Transfer successfully completed | -| `FAILED` | Transfer failed (see error details) | - -## Cross-Currency Transfers - -Use the quotes flow when sending funds with currency conversion. This locks in an exchange rate and provides all details needed to execute the transfer. - -### When to use cross-currency transfers - -- Converting USD to EUR, MXN, BRL, or other supported currencies -- Sending international payments with automatic currency conversion -- Need to lock in a specific exchange rate for the transfer - -### Create and execute a quote - - - - Request a quote to lock in the exchange rate and get transfer details: - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Payment for services - Invoice #1234" - }' -``` - -```json Success (201 Created) -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000025", - "status": "PENDING", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "sendingAmount": { - "amount": 10000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "receivingAmount": { - "amount": 9200, - "currency": { - "code": "EUR", - "name": "Euro", - "symbol": "€", - "decimals": 2 - } - }, - "exchangeRate": 0.92, - "fee": { - "amount": 50, - "currency": { - "code": "USD", - "symbol": "$", - "decimals": 2 - } - }, - "expiresAt": "2025-10-03T15:15:00Z", - "createdAt": "2025-10-03T15:00:00Z", - "description": "Payment for services - Invoice #1234" -} -``` - - - **Locked currency side** determines which amount is fixed: - - `SENDING`: Lock the sending amount (receiving amount calculated based on exchange rate) - - `RECEIVING`: Lock the receiving amount (sending amount calculated based on exchange rate) - - - - - Before executing, review the quote to ensure: - -- Exchange rate is acceptable -- Fees are as expected -- Receiving amount meets requirements -- Quote hasn't expired (check `expiresAt`) - - - Quotes typically expire after 15 minutes. If expired, create a new quote to get an updated exchange rate. - - - - - Confirm and execute the quote to initiate the transfer: - - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes/Quote:019542f5-b3e7-1d02-0000-000000000025/execute' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -```json Success (200 OK) -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000025", - "status": "PROCESSING", - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "sendingAmount": { - "amount": 10000, - "currency": { - "code": "USD", - "symbol": "$", - "decimals": 2 - } - }, - "receivingAmount": { - "amount": 9200, - "currency": { - "code": "EUR", - "symbol": "€", - "decimals": 2 - } - }, - "exchangeRate": 0.92, - "executedAt": "2025-10-03T15:05:00Z" -} -``` - - - Once executed, the quote creates a transaction and the transfer begins processing. The `transactionId` can be used to track the payment. - - - - - Track the transfer using webhooks or by polling the transaction: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000030' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -You'll receive a webhook when the transaction completes: - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "COMPLETED", - "sentAmount": { - "amount": 10000, - "currency": { "code": "USD", "symbol": "$", "decimals": 2 } - }, - "receivedAmount": { - "amount": 9200, - "currency": { "code": "EUR", "symbol": "€", "decimals": 2 } - }, - "exchangeRate": 0.92, - "settledAt": "2025-10-03T15:30:00Z", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000025" - }, - "timestamp": "2025-10-03T15:31:00Z" -} -``` - - - - -### Quote statuses - -| Status | Description | -| ------------ | ------------------------------------------ | -| `PENDING` | Quote created, awaiting execution | -| `PROCESSING` | Quote executed, transfer in progress | -| `COMPLETED` | Transfer successfully completed | -| `FAILED` | Transfer failed (funds returned to source) | -| `EXPIRED` | Quote expired without execution | - -## Checking Payment Status - -### Using webhooks (recommended) - -Configure a webhook endpoint to receive real-time notifications when payment status changes: - -```javascript -// Your webhook endpoint -app.post("/webhooks/grid", (req, res) => { - const { type, transaction } = req.body; - - if (type === "OUTGOING_PAYMENT") { - const { id, status, settledAt } = transaction; - - switch (status) { - case "COMPLETED": - console.log(`Payment ${id} completed at ${settledAt}`); - // Update your database, notify customer, etc. - break; - - case "FAILED": - console.log(`Payment ${id} failed`); - // Handle failure, refund, notify customer - break; - - case "PROCESSING": - console.log(`Payment ${id} is processing`); - // Optional: Update UI to show processing state - break; - } - } - - res.status(200).json({ received: true }); -}); -``` - - - See the [Webhooks guide](/payouts-and-b2b/platform-tools/webhooks) for complete - webhook implementation details including signature verification. - - -### Polling transactions - -If webhooks aren't available, poll the transaction endpoint: - -```javascript -async function checkPaymentStatus(transactionId) { - const response = await fetch( - `https://api.lightspark.com/grid/2025-10-13/transactions/${transactionId}`, - { - headers: { - Authorization: `Basic ${credentials}`, - }, - } - ); - - const transaction = await response.json(); - return transaction.status; -} - -// Poll every 10 seconds -const pollInterval = setInterval(async () => { - const status = await checkPaymentStatus( - "Transaction:019542f5-b3e7-1d02-0000-000000000015" - ); - - if (status === "COMPLETED" || status === "FAILED") { - clearInterval(pollInterval); - // Handle completion - } -}, 10000); -``` - - - Polling can delay status updates and increase API usage. Use webhooks for - production applications whenever possible. - - -## Best Practices - - - -Verify the internal account has sufficient balance to avoid failed transfers: - -```javascript -async function checkBalance(accountId, requiredAmount) { - const response = await fetch( - `https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts`, - { - headers: { Authorization: `Basic ${credentials}` }, - } - ); - - const { data } = await response.json(); - const account = data.find((a) => a.id === accountId); - - if (account.balance.amount < requiredAmount) { - throw new Error("Insufficient balance"); - } - - return true; -} -``` - -This prevents unnecessary API calls and provides better user feedback. - - - - -Store quote IDs in your database to prevent accidental duplicate executions: - -```javascript -async function executeQuoteOnce(quoteId) { - // Check if already executed - const existing = await db.quotes.findOne({ quoteId }); - if (existing?.executed) { - throw new Error("Quote already executed"); - } - - // Execute and mark as executed - const result = await executeQuote(quoteId); - await db.quotes.update( - { quoteId }, - { executed: true, transactionId: result.transactionId } - ); - - return result; -} -``` - - - - -Quotes expire after a short period. Always check expiration before executing: - -```javascript -async function executeQuoteWithCheck(quoteId) { - const quote = await getQuote(quoteId); - - if (new Date(quote.expiresAt) < new Date()) { - // Quote expired, create a new one - const newQuote = await createQuote({ - source: quote.source, - destination: quote.destination, - lockedCurrencySide: quote.lockedCurrencySide, - lockedCurrencyAmount: quote.lockedCurrencyAmount, - }); - - return executeQuote(newQuote.id); - } - - return executeQuote(quoteId); -} -``` - - - - -Always include meaningful descriptions to help with reconciliation: - -```javascript -const description = [ - `Invoice #${invoiceId}`, - `Customer: ${customerName}`, - `Date: ${new Date().toISOString().split("T")[0]}`, -].join(" | "); - -await createQuote({ - // ... other fields - description: description, -}); -``` - -This makes it easier to match payments in your accounting system and provides context when reviewing transactions. - - - - -Always save transaction and quote IDs for audit trails and support: - -```javascript -const quote = await createQuote(quoteData); - -// Save to your database immediately -await db.payments.create({ - quoteId: quote.id, - customerId: customer.id, - amount: quote.sendingAmount.amount, - currency: quote.sendingAmount.currency.code, - status: "pending", - createdAt: new Date(), -}); - -const execution = await executeQuote(quote.id); - -// Update with transaction ID -await db.payments.update( - { quoteId: quote.id }, - { transactionId: execution.transactionId, status: "processing" } -); -``` - - - - -## Next Steps - - - - Handle payment failures and error scenarios - - - - Query and filter transaction history - - - - Match payments with your internal systems - - diff --git a/mintlify/payouts-and-b2b/platform-tools/postman-collection.mdx b/mintlify/payouts-and-b2b/platform-tools/postman-collection.mdx deleted file mode 100644 index d11e88a6..00000000 --- a/mintlify/payouts-and-b2b/platform-tools/postman-collection.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Postman Collection" ---- - -import PostmanCollection from '/snippets/postman-collection.mdx'; - - - - diff --git a/mintlify/payouts-and-b2b/platform-tools/sandbox-testing.mdx b/mintlify/payouts-and-b2b/platform-tools/sandbox-testing.mdx deleted file mode 100644 index 3d6d5aa6..00000000 --- a/mintlify/payouts-and-b2b/platform-tools/sandbox-testing.mdx +++ /dev/null @@ -1,305 +0,0 @@ ---- -title: "Sandbox Testing" -description: "Test your payouts integration in the Grid sandbox environment" ---- - -## Overview - -The Grid sandbox environment allows you to test your payouts integration without moving real money. All API endpoints work the same way in sandbox as they do in production, but money movements are simulated and you can control test scenarios using special test values. - -## Getting Started with Sandbox - -### Sandbox Credentials - -To use the sandbox environment: - -1. Contact Lightspark to get your inital sandbox credentials configured. Email [support@lightspark.com](mailto:support@lightspark.com) to get started. -2. Add your sandbox API token and secret to your environment variables. -3. Use the normal production base URL: `https://api.lightspark.com/grid/2025-10-13` -4. Authenticate using your sandbox token with HTTP Basic Auth - -## Simulating Money Movements - -### Funding Internal Accounts - -In production, internal accounts are funded by following the payment instructions (bank transfer, wire, etc.). In sandbox, you can instantly add funds to any internal account using the following endpoint: - -```bash -POST /sandbox/internal-accounts/{accountId}/fund - -{ - "amount": 100000 # $1,000 in cents -} -``` - -**Example:** - -```bash -curl -X POST https://api.lightspark.com/grid/2025-10-13/sandbox/internal-accounts/InternalAccount:abc123/fund \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "amount": 100000 - }' -``` - -This endpoint returns the updated `InternalAccount` object with the new balance. - -Alternatively, you can also fund internal accounts using the `/quotes` or `/transfer-in` endpoints as described below. - -## Testing Transfer Scenarios - -### Adding Test External Accounts - -The flows for creating external accounts in sandbox are the same as in production. -However, when creating external accounts in sandbox, you can use special account number patterns to simulate different -transfer behaviors. The **last 3 digits** of the account number determine the test scenario: - -| Last Digits | Behavior | Use Case | -|-------------|----------|----------| -| **002** | Insufficient funds | Transfer-in fails immediately | -| **003** | Account closed/invalid | All transfers fail immediately | -| **004** | Transfer rejected | Bank rejects the transfer | -| **005** | Timeout/delayed failure | Transaction stays pending ~30s, then fails | -| **Any other** | Success | All transfers complete normally | - -**Example - Creating a Test Account with Insufficient Funds:** - -```bash -POST /customers/external-accounts - -{ - "customerId": "Customer:123", - "currency": "USD", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "000000002", // Will trigger insufficient funds - "routingNumber": "110000000", - "accountCategory": "CHECKING", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Test User", - "address": { - "line1": "123 Test St", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } -} -``` - - -These patterns apply to the primary identifier for any account type: US account numbers, IBANs, CLABEs, Spark wallet addresses, etc. Just ensure the identifier ends with the appropriate test digits. -For scenarios like PIX and UPI, where there's a domain part involved, append the test digits to the user name part. For example, if testing email addresses as a PIX key, the full identifier would be -"testuser.002@pix.com.br" to trigger the insufficient funds scenario. - - -### Testing Transfer-In (Pull from External Account) - -When you call `/transfer-in` with an external account created using test patterns, the transfer will complete instantly in sandbox with the behavior determined by the account number: - -```bash -POST /transfer-in - -{ - "source": { - "accountId": "ExternalAccount:abc123" // Uses test pattern from creation - }, - "destination": { - "accountId": "InternalAccount:xyz789" - }, - "amount": 10000 // $100 in cents -} -``` - -**Expected Behaviors:** - -- **Success (default)**: Transaction completes immediately with status `COMPLETED` -- **Insufficient funds (002)**: Transaction fails immediately with appropriate error -- **Account closed (003)**: Transaction fails immediately with account validation error -- **Transfer rejected (004)**: Transaction fails immediately with rejection error -- **Timeout (009)**: Transaction shows `PENDING` status for ~30 seconds, then transitions to `FAILED` - -### Testing Transfer-Out (Push to External Account) - -Transfer-out works the same way - the destination external account's test pattern determines the outcome: - -```bash -POST /transfer-out - -{ - "source": { - "accountId": "InternalAccount:xyz789" - }, - "destination": { - "accountId": "ExternalAccount:abc123" // Uses test pattern - }, - "amount": 10000 -} -``` - -The transfer will instantly simulate the bank transfer process and complete with the appropriate status based on the external account's test pattern. - -## Testing Cross-Currency Quotes - -### Creating Quotes with Test Accounts - -When creating quotes with the `externalAccountDetails` destination type, you can provide test account patterns inline: - -```bash -POST /quotes - -{ - "source": { - "accountId": "InternalAccount:abc123" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:123", - "currency": "EUR", - "accountInfo": { - "accountType": "IBAN_ACCOUNT", - "iban": "DE89370400440532013003", // Ends in 003 = account closed - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Test User" - } - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 -} -``` - -### Executing Quotes in Sandbox - -For quotes from an external account source, execute as in production via `/quotes/{quoteId}/execute`. The sandbox will: - -1. Instantly process the currency conversion -2. Apply the test behavior based on any external accounts involved -3. Update transaction statuses immediately (no waiting for bank processing) -4. Trigger webhooks for state changes - -For quotes with payment instructions (no source account), use the existing `/sandbox/send` endpoint to simulate payment: - -```bash -POST /sandbox/send - -{ - "reference": "UMA-Q12345-REF", // From quote payment instructions - "currencyCode": "USD", - "currencyAmount": 100000 -} -``` - -## Testing Webhooks - -All webhook events fire normally in sandbox. To test your webhook endpoint: - -1. Configure your webhook URL in the dashboard -2. Perform actions that trigger webhooks (transfers, quote execution, etc.) -3. Receive webhook events at your endpoint -4. Verify signature using the sandbox public key - -You can also manually trigger a test webhook: - -```bash -POST /webhooks/test - -{ - "url": "https://your-app.com/webhooks" -} -``` - -## Common Testing Workflows - -### Complete Payout Flow Test - -Here's a complete test workflow for a USD → EUR payout: - -1. **Create customer and internal accounts** (via regular API) - -2. **Fund customer's USD internal account:** - ```bash - POST /sandbox/internal-accounts/InternalAccount:customer-usd/fund - { "amount": 100000 } # $1,000 - ``` - -3. **Create a test external EUR account:** - ```bash - POST /customers/external-accounts - # Use default account number for success case - ``` - -4. **Create and execute a quote:** - ```bash - POST /quotes - # USD internal → EUR external - - POST /quotes/{quoteId}/execute - ``` - -5. **Verify transaction status and webhooks** - -### Testing Error Scenarios - -Test each failure mode systematically: - -```bash -# 1. Test insufficient funds -# Create external account ending in 002 -POST /customers/external-accounts { "accountNumber": "000000002" } - -# Attempt transfer-in - should fail immediately -POST /transfer-in - -# 2. Test account closed -# Create external account ending in 003 -POST /customers/external-accounts { "accountNumber": "000000003" } - -# Attempt transfer-out - should fail immediately -POST /transfer-out - -# 3. Test timeout scenario -# Create external account ending in 005 -POST /customers/external-accounts { "accountNumber": "000000005" } - -# Attempt transfer - should pend then fail after ~30s -POST /transfer-in -# Check status immediately - will show PENDING -GET /transactions/{transactionId} -# Wait 30s, check again - will show FAILED -``` - -## Sandbox Limitations - -While sandbox closely mimics production, there are some differences: - -- **Instant settlement**: All transfers complete immediately (success cases) or fail immediately (error cases), except timeout scenarios (005) -- **No real bank validation**: Account numbers aren't validated against real banking networks -- **Simplified KYC**: KYC processes are simulated and complete instantly. You must add customers via the `/customers` endpoint, rather than using the KYC link flow. -- **Fixed exchange rates**: Currency conversion rates may not reflect real-time market rates. - - -Do not try sending money to any sandbox addresses or accounts. These are not real addresses and will not receive money. - - -## Moving to Production - -When you're ready to move to production: - -1. Generate production API tokens in the dashboard -2. Swap those credentials for the sandbox credentials in your environment variables -3. Remove any sandbox-specific test patterns from your code -4. Configure production webhook endpoints -5. Test with small amounts first - -## Next Steps - -- Review [Webhooks](/payouts-and-b2b/platform-tools/webhooks) for event handling -- Check out the [Postman Collection](/payouts-and-b2b/platform-tools/postman-collection) for API examples -- See [Error Handling](/payouts-and-b2b/payment-flow/error-handling) for production error strategies diff --git a/mintlify/payouts-and-b2b/platform-tools/webhooks.mdx b/mintlify/payouts-and-b2b/platform-tools/webhooks.mdx deleted file mode 100644 index e636c6ff..00000000 --- a/mintlify/payouts-and-b2b/platform-tools/webhooks.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Webhooks" ---- - -import Webhooks from '/snippets/webhooks.mdx'; - - \ No newline at end of file diff --git a/mintlify/payouts-and-b2b/quickstart.mdx b/mintlify/payouts-and-b2b/quickstart.mdx deleted file mode 100644 index 99782575..00000000 --- a/mintlify/payouts-and-b2b/quickstart.mdx +++ /dev/null @@ -1,412 +0,0 @@ ---- -title: "Quickstart" -description: "Send your first cross-border payment" ---- - -import KycRegulated from '/snippets/kyc/kyc-regulated.mdx'; -import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx'; - -This quickstart covers an example of sending a prefunded cross-border payout for a business customer on an unregulated platform. - -## Understanding Entity Mapping for B2B Payouts - -In this guide, the entities map as follows: - -| Entity Type | Who They Are | In This Example | -|------------|--------------|----------------| -| **Platform** | Your payouts platform | Your company providing AP automation | -| **Customer** | Business sending payments | Your client company (e.g., Acme Corp) | -| **External Account** | Vendors receiving payments | Maria Garcia (freelance contractor in Mexico) | - -**Flow**: Your customer (a business) funds their internal account → uses your platform to send payments → to their vendors' external bank accounts. - -## Get API credentials -Create Sandbox API credentials in the dashboard, then set environment variables for local use. - - -```bash -export GRID_BASE_URL="https://api.lightspark.com/grid/2025-10-13" -export GRID_CLIENT_ID="YOUR_SANDBOX_CLIENT_ID" -export GRID_CLIENT_SECRET="YOUR_SANDBOX_CLIENT_SECRET" -``` - - -Use Basic Auth in cURL with `-u "$GRID_CLIENT_ID:$GRID_API_SECRET"`. - - -## Onboard a Customer -Onboard a customer using the hosted KYC/KYB link flow. -### Generate KYC Link - Call the `/customers/kyc-link` endpoint with your `redirectUri` parameter to generate a hosted KYC URL for your customer. - - - The `redirectUri` parameter is embedded in the generated KYC URL and will be used to automatically redirect the customer back to your application after they complete verification. - - ```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers/kyc-link?redirectUri=https://yourapp.com/onboarding-complete&platformCustomerId=019542f5-b3e7-1d02-0000-000000000001" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` -**Response:** - -```json -{ - "kycUrl": "https://kyc.lightspark.com/onboard/abc123def456", - "platformCustomerId": "019542f5-b3e7-1d02-0000-000000000001" -} -``` -### Redirect Customer - Redirect your customer to the returned `kycUrl` where they can complete their identity verification in the hosted interface. - - - The KYC link is single-use and expires after a limited time period for security. - - -### Customer Completes Verification - The customer completes the identity verification process in the hosted KYC interface, providing required documents and information. - - - The hosted interface handles document collection, verification checks, and compliance requirements automatically. - - - - After verification processing, you'll receive a KYC status webhook notification indicating the final verification result. - - -### Redirect back to your app - Upon successful KYC completion, the customer is automatically redirected to your specified `redirectUri` URL. - - On your redirect page, handle the completed KYC flow and integrate the new customer into your application. - - - The customer account will be automatically created by the system upon successful KYC completion. You can identify the new customer using your `platformCustomerId` or other identifiers. - - -## Get the Customer's Internal Account - -Once the customer is created, internal accounts will automatically be created on their behalf. Get their internal account in the desired currency for funding instructions. - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/internal-account?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USD" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -**Response:** -```json -{ - "data": [ - { - "id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "balance": { - "amount": 0, // USD balance in cents - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "fundingPaymentInstructions": [ - { - "instructionsNotes": "Include the reference code in your ACH transfer memo", - "accountOrWalletInfo": { - "reference": "FUND-ABC123", - "accountType": "US_ACCOUNT", - "accountNumber": "9876543210", - "routingNumber": "021000021", - "accountHolderName": "Lightspark Payments FBO John Doe", - "bankName": "JP Morgan Chase" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ], - "createdAt": "2025-10-03T12:00:00Z", - "updatedAt": "2025-10-03T12:00:00Z" - } - ], - "hasMore": false, - "totalCount": 1 -} -``` - -The `fundingPaymentInstructions` provide the bank account details and reference code needed to fund -this internal account via ACH or wire transfer from the customer's bank. It might also include stablecoin -funding details for instant funding of the internal account. - -## Fund the Internal Account - -For this quickstart, we'll fund the account using the `/sandbox/internal-accounts/{accountId}/fund` endpoint to simulate receiving funds. - -In production, your customer would initiate a transfer from their bank or wallet to the account details provided in the funding instructions, -making sure to include the reference code `FUND-ABC123` in the transfer memo if applicable. - -```bash -# Sandbox: fund internal account directly -curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/internal-accounts/InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965/fund" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "currencyCode": "USD", - "currencyAmount": 100000, - "reference": "FUND-ABC123" - }' -``` - -During the funding process, you'll receive transaction status update webhooks. - -**Webhook Notification:** -```json -{ - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000010", - "status": "COMPLETED", - "type": "INCOMING", - "receivedAmount": { - "amount": 100000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "settledAt": "2025-10-03T14:30:00Z", - "createdAt": "2025-10-03T14:25:00Z", - "description": "Internal account funding" - }, - "timestamp": "2025-10-03T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "INCOMING_PAYMENT" -} -``` - -The internal account now has a balance of $1,000.00 (100000 cents). -## Add the beneficiary as an External Account - -Now add the beneficiary bank account where you want to send the funds. In this example, we'll add a -Mexican CLABE account as the external account. - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "MXN", - "platformAccountId": "maria_garcia_account", - "accountInfo": { - "accountType": "CLABE", - "clabeNumber": "123456789012345678", - "bankName": "BBVA Mexico", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Maria Garcia", - "birthDate": "1990-01-01", - "nationality": "MX", - "address": { - "line1": "Av. Reforma 123", - "city": "Ciudad de México", - "state": "CDMX", - "postalCode": "06600", - "country": "MX" - } - } - } - }' -``` - -**Response:** -```json -{ - "id": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "status": "ACTIVE", - "currency": "MXN", - "platformAccountId": "maria_garcia_account", - "accountInfo": { - "accountType": "CLABE", - "clabeNumber": "123456789012345678", - "bankName": "BBVA Mexico", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Maria Garcia", - "birthDate": "1990-01-01", - "nationality": "MX", - "address": { - "line1": "Av. Reforma 123", - "city": "Ciudad de México", - "state": "CDMX", - "postalCode": "06600", - "country": "MX" - } - } - } -} -``` -## Create a quote -Create a quote to lock in the exchange rate, fees, and get the transfer details. - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "lookupId": "LookupRequest:019542f5-b3e7-1d02-0000-000000000009", # ID from the lookup step - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" # USD internal account - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "MXN" - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 50000, - "description": "Payment to Maria Garcia for services" - }' -``` - - - **Amount Locking**: You can lock either the sending amount (`SENDING`) or receiving amount (`RECEIVING`). - In this example, we're locking the sending amount to exactly $500.00 USD (50000 cents). Alternatively, - you can lock the receiving amount to ensure that the receiver receives exactly some amount of the - destination currency. - - -**Response:** -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "status": "PENDING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "MXN" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "MXN", - "name": "Mexican Peso", - "symbol": "$", - "decimals": 2 - }, - "totalSendingAmount": 50000, - "totalReceivingAmount": 861250, - "exchangeRate": 17.25, - "feesIncluded": 250, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000015" -} -``` - -The quote shows: -- **Sending**: \$500.00 USD (including $2.50 fee) -- **Receiving**: $8,612.50 MXN -- **Exchange rate**: 17.25 MXN per USD -- **Quote expires**: In 5 minutes - - - Quotes typically expire in 1-5 minutes. Make sure to execute the quote before the `expiresAt` timestamp, or you'll need to create a new quote. - - -## Execute the quote -Execute the quote to initiate the transfer from the internal account to the external bank account. - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/Quote:019542f5-b3e7-1d02-0000-000000000006/execute" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -**Response:** -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "status": "PROCESSING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "MXN" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "MXN", - "name": "Mexican Peso", - "symbol": "$", - "decimals": 2 - }, - "totalSendingAmount": 50000, - "totalReceivingAmount": 861250, - "exchangeRate": 17.25, - "feesIncluded": 250, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000015" -} -``` - -The quote status changes to `PROCESSING` and the transfer is initiated. You can track the status by: -1. Polling the quote endpoint: `GET /quotes/{quoteId}` -2. Waiting for webhook notifications - -**Completion Webhook:** -```json -{ - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000015", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 861250, - "currency": { - "code": "MXN", - "name": "Mexican Peso", - "symbol": "$", - "decimals": 2 - } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "settledAt": "2025-10-03T15:02:30Z", - "createdAt": "2025-10-03T15:00:00Z", - "description": "Payment to Maria Garcia for services", - "exchangeRate": 17.25, - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006" - }, - "timestamp": "2025-10-03T15:03:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000025", - "type": "OUTGOING_PAYMENT" -} -``` - -Congrats, you've sent a real time cross-border payout to a Mexican bank account! diff --git a/mintlify/payouts-and-b2b/terminology.mdx b/mintlify/payouts-and-b2b/terminology.mdx deleted file mode 100644 index d90a2a1d..00000000 --- a/mintlify/payouts-and-b2b/terminology.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Core Concepts" -description: "Core concepts and terminology for the Grid API" ---- - -import Terminology from '/snippets/terminology.mdx'; - - \ No newline at end of file diff --git a/mintlify/platform-overview/core-concepts/account-model.mdx b/mintlify/platform-overview/core-concepts/account-model.mdx deleted file mode 100644 index d9be0cb1..00000000 --- a/mintlify/platform-overview/core-concepts/account-model.mdx +++ /dev/null @@ -1,289 +0,0 @@ ---- -title: "Account Model" -description: "Internal accounts, external accounts, and how they work together" ---- - -import InternalAccounts from '/snippets/internal-accounts.mdx'; - -Grid uses two types of accounts: **internal accounts** (Grid-managed balances) and **external accounts** (connected bank accounts and wallets). Understanding when to use each type is key to building efficient payment flows. - -## Internal Accounts - - - -## External Accounts - -**External accounts** represent bank accounts, crypto wallets, or other payment instruments outside of Grid for on-ramping or off-ramping funds. - -### Characteristics - -- Represent real-world accounts (bank accounts, crypto wallets) -- Used as funding sources or payout destinations -- Subject to verification and compliance screening -- Have associated beneficiary information -- Status indicates readiness for use - -### Supported Account Types - - - - ```json - { - "accountType": "US_ACCOUNT", - "currency": "USD", - "accountNumber": "1234567890", - "routingNumber": "110000000", - "accountCategory": "CHECKING", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Alice Johnson", - "address": { - "line1": "123 Main St", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } - ``` - - - - ```json - { - "accountType": "IBAN", - "currency": "EUR", - "iban": "DE89370400440532013000", - "swiftBic": "DEUTDEFF", - "bankName": "Deutsche Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Hans Schmidt" - } - } - ``` - - - - ```json - { - "accountType": "PIX", - "currency": "BRL", - "pixKey": "user@example.com", - "pixKeyType": "EMAIL", - "bankName": "Nubank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Maria Silva", - "taxId": "12345678900" - } - } - ``` - - - - ```json - { - "accountType": "CLABE", - "clabeNumber": "012345678901234567", - "bankName": "BBVA Mexico", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Carlos Rodriguez" - } - } - ``` - - - - ```json - { - "accountType": "UPI", - "currency": "INR", - "vpa": "user@paytm", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Priya Sharma" - } - } - ``` - - - - ```json - { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu", - "currency": "BTC" - } - ``` - - - -### Creating External Accounts - -```bash Creating a US bank account -curl -X POST https://api.lightspark.com/grid/2025-10-13/customers/external-accounts \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "9876543210", - "routingNumber": "110000000", - "accountCategory": "CHECKING", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Alice Johnson", - "address": { - "line1": "123 Main St", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } - }' -``` - -**Response:** - -```json -{ - "id": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD", - "status": "PENDING", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "****3210", - "routingNumber": "110000000" - } -} -``` - -### External Account Status - -External accounts progress through verification: - -``` -PENDING → ACTIVE -``` - -- **PENDING**: Created, undergoing verification/screening -- **ACTIVE**: Verified, ready for transactions -- **INACTIVE**: Disabled (can be reactivated) -- **REJECTED**: Failed verification - -You'll receive `ACCOUNT_STATUS` webhooks as status changes. - -### Using External Accounts - - - - Send funds from internal account to external account: - - ```bash - POST /transfer-out - - { - "source": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "destination": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"}, - "amount": 100000 - } - ``` - - Or via quote for cross-currency: - - ```bash - POST /quotes - - { - "source": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "destination": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"}, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 - } - ``` - - - - Pull funds from external account to internal account: - - ```bash - POST /transfer-in - - { - "source": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123"}, - "destination": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "amount": 100000 - } - ``` - - Only works for "pullable" external accounts (e.g., linked via Plaid, debit cards). - - - ---- - -## Account Combinations - -Common patterns for combining internal and external accounts: - -### Pattern 1: Prefunded Payouts - -**Use case:** Send payouts from customer's prefunded balance - -``` -Customer Internal Account (USD) → External Bank Account (EUR) -``` - -1. Customer funds internal account via ACH -2. Create quote: Internal USD → External EUR -3. Execute quote -4. Recipient receives EUR in their bank - -### Pattern 2: JIT Funded Payouts - -**Use case:** On-demand payments without maintaining balance - -``` -Customer → Payment Instructions → Internal Account → External Account -``` - -1. Create quote with `source: {customerId}` -2. Grid provides payment instructions -3. Customer sends funds to instructions -4. Quote auto-executes when received -5. Recipient receives payout - -### Pattern 3: Platform Rewards - -**Use case:** Platform distributes Bitcoin rewards - -``` -Platform Internal Account (USD) → Customer External Wallet (BTC) -``` - -1. Platform funds its own internal USD account -2. Create quote: Platform Internal USD → Customer Spark Wallet -3. Execute with `immediatelyExecute: true` -4. Customer receives BTC instantly - -### Pattern 4: Crypto On-Ramp - -**Use case:** Customer buys Bitcoin - -``` -Customer External Bank → Customer Internal Account (USD) → Customer External Wallet (BTC) -``` - -1. Customer links bank via Plaid -2. Pull funds to internal USD account -3. Create quote: Internal USD → External Spark Wallet -4. Execute quote -5. BTC delivered to customer's wallet diff --git a/mintlify/platform-overview/core-concepts/currencies-and-rails.mdx b/mintlify/platform-overview/core-concepts/currencies-and-rails.mdx deleted file mode 100644 index 7dc70720..00000000 --- a/mintlify/platform-overview/core-concepts/currencies-and-rails.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Currencies & Payment Rails" -description: "Supported currencies, countries, and payment methods" ---- - -import CountrySupport from '/snippets/country-support.mdx'; - -Grid supports a wide range of fiat currencies and cryptocurrencies, with automatic routing across optimal payment rails based on currency, destination, and speed requirements. - -## Supported Currencies - - - - -Grid automatically selects the optimal rail based on currency, country, and amount. You don't need to specify the rail in your API requests. - - -## Currency Conversion - -Grid handles currency conversion automatically through the quote system: - -### Exchange Rates - -- **Real-time rates**: Grid provides live exchange rates based on market conditions -- **Rate locking**: Quotes lock rates for 1-15 minutes depending on payment type -- **Transparency**: Exact rates and fees shown before execution -- **No hidden fees**: What you see in the quote is what you pay - -### Example Conversion Flow - -```bash -POST /quotes - -{ - "source": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "destination": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "MXN"}, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 -} -``` - -**Response:** - - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020", - "status": "PROCESSING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "MXN" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "MXN", - "name": "Mexican Peso", - "symbol": "$", - "decimals": 8 - }, - "totalSendingAmount": 100, - "totalReceivingAmount": 17250, - "exchangeRate": 17.25, - "feesIncluded": 5, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025" -} -``` - - -This shows: Send $1,000 → Receive $17,250 (at rate 17.25), $250 fee. diff --git a/mintlify/platform-overview/core-concepts/entities.mdx b/mintlify/platform-overview/core-concepts/entities.mdx deleted file mode 100644 index e1832793..00000000 --- a/mintlify/platform-overview/core-concepts/entities.mdx +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: "Entities & Relationships" -description: "Understanding Grid's core data model" ---- - -import Terminology from '/snippets/terminology.mdx'; - - - -## Platform Configuration - -Your **platform configuration** defines global settings for your Grid integration: - -- **Webhook endpoint**: URL where Grid sends event notifications -- **Supported currencies**: Which currencies your platform offers -- **UMA domain** (optional): Your domain for UMA addresses (e.g., `yourplatform.com`) -- **Required counterparty fields** (UMA only): Information required from senders for compliance - -Platform configuration is managed via the dashboard or by contacting Lightspark support. - -## ID Format - -All Grid entities use a consistent ID format: - -``` -EntityType:UUID -``` - -Examples: -- `Customer:019542f5-b3e7-1d02-0000-000000000001` -- `InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965` -- `Transaction:019542f5-b3e7-1d02-0000-000000000030` -- `Quote:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123` - -This format makes IDs self-documenting and easier to debug. - -## Platform Customer ID - -When creating customers, you can provide your own `platformCustomerId`: - -```json -{ - "platformCustomerId": "user_12345_from_my_system", - "customerType": "INDIVIDUAL", - "fullName": "Alice Johnson" -} -``` - -This allows you to: -- Map Grid customers to your internal user IDs -- Query transactions by your own customer identifiers -- Maintain referential integrity between systems - -Grid returns both IDs in all customer-related responses: -```json -{ - "id": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "user_12345_from_my_system" -} -``` - -## Amount Representation - -All monetary amounts in Grid are represented in the **smallest currency unit**: - - - - **Cents, pence, centavos, etc.** - - ```json - { - "amount": 50000, - "currency": { - "code": "USD", - "symbol": "$", - "decimals": 2 - } - } - ``` - - This represents **$500.00** (50000 cents) - - - - **Satoshis for Bitcoin** - - ```json - { - "amount": 10000000, - "currency": { - "code": "BTC", - "symbol": "₿", - "decimals": 8 - } - } - ``` - - This represents **0.1 BTC** (10,000,000 satoshis) - - - -The `decimals` field tells you how many decimal places to use when displaying the amount: - -```javascript -function formatAmount(amount, currency) { - const value = amount / Math.pow(10, currency.decimals); - return `${currency.symbol}${value.toFixed(currency.decimals)}`; -} - -// formatAmount(50000, {code: "USD", symbol: "$", decimals: 2}) → "$500.00" -// formatAmount(10000000, {code: "BTC", symbol: "₿", decimals: 8}) → "₿0.10000000" -``` - -## Timestamps - -All timestamps in Grid use **ISO 8601 format with UTC timezone**: - -```json -{ - "createdAt": "2025-10-03T15:00:00Z", - "settledAt": "2025-10-03T15:30:00Z" -} -``` - -Key timestamp fields: -- **createdAt**: When the entity was created -- **updatedAt**: Last modification time -- **settledAt**: When a transaction completed (transactions only) -- **expiresAt**: When a quote expires (quotes only) - diff --git a/mintlify/platform-overview/core-concepts/quote-system.mdx b/mintlify/platform-overview/core-concepts/quote-system.mdx deleted file mode 100644 index 2e9a3b57..00000000 --- a/mintlify/platform-overview/core-concepts/quote-system.mdx +++ /dev/null @@ -1,423 +0,0 @@ ---- -title: "Quote System" -description: "How exchange rates, pricing, and payment execution work" ---- - -Quotes are Grid's mechanism for providing locked-in exchange rates, transparent fee calculations, and payment instructions. Understanding quotes is essential for all cross-currency or crypto-involved transactions. - -## What is a Quote? - -A **quote** locks in: -- An exchange rate between two currencies -- Total fees for the transaction -- Exact amounts to be sent and received -- Payment instructions (if JIT funding is needed) -- An expiration time (typically 1-5 minutes, up to 15 minutes depending on the payment type) - -Quotes ensure that your customers know exactly what they'll pay and what the recipient will receive before committing to a transaction. - -## When Do You Need a Quote? - - - - Use quotes for: - - **Cross-currency transfers** (USD → EUR, BRL → MXN) - - **Fiat-to-crypto conversion** (USD → BTC) - - **Crypto-to-fiat conversion** (BTC → USD) - - **UMA payments** (always require quotes) - - **JIT funded payments** (need payment instructions) - - These scenarios involve currency conversion, exchange rate risk, or complex routing. - - - - For same-currency transfers, use simpler endpoints: - - `POST /transfer-out` - Send from internal to external account (same currency) - - `POST /transfer-in` - Pull from external to internal account (same currency) - - No quote needed because there's no currency conversion. - - - -## Creating a Quote - -### Basic Cross-Currency Quote - -This is a basic quote for a cross-currency transfer from an internal account to an external account, -which were pre-created as described in the [Account Model](/platform-overview/core-concepts/account-model) section. - -```bash -curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "currency": "BTC", - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123" - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 - }' -``` - -**Response:** - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020", - "status": "PROCESSING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "BTC" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "BTC", - "name": "Bitcoin", - "symbol": "₿", - "decimals": 8 - }, - "totalSendingAmount": 100000, - "totalReceivingAmount": 829167, - "exchangeRate": 0.00000833, - "feesIncluded": 500, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025" -} -``` - -This quote says: Send $1,000 USD, recipient receives 0.00829167 BTC, fees are $5.00, expires in 5 minutes. - -### Locked Currency Side - -You can lock either the **sending** or **receiving** amount: - - - - **Use when:** Customer knows exactly how much they want to send - - ```json - { - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 - } - ``` - - Grid calculates what the recipient will receive based on current exchange rates. - - - - **Use when:** Recipient must receive an exact amount (e.g., invoice payment) - - ```json - { - "lockedCurrencySide": "RECEIVING", - "lockedCurrencyAmount": 92000 - } - ``` - - Grid calculates how much the sender must send to ensure recipient gets exactly €920. - - - -## Funding Models - -Grid supports two funding models for quotes: - -### Prefunded (From Internal Account) - -Source is an existing internal account with available balance: - -```json -{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - } -} -``` - -- Funds are debited immediately when quote is executed -- No payment instructions needed -- Best for: Customers with pre-loaded balances - -### Just-In-Time (JIT) Funding - -Source is the customer ID or the platform itself — Grid provides payment instructions: - -```json -{ - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001" - } -} -``` - -**Quote response includes payment instructions:** - -```json -{ - "quoteId": "Quote:...", - "paymentInstructions": [ - { - "instructionsNotes": "Please ensure the reference code is included in the payment memo/description field", - "accountOrWalletInfo": { - "reference": "UMA-Q12345-REF", - "accountType": "US_ACCOUNT", - "accountNumber": "9876543210", - "routingNumber": "110000000", - "accountCategory": "CHECKING", - "bankName": "Chase Bank" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ] -} -``` - -- Customer sends funds to provided account with reference -- Quote executes automatically when Grid receives payment -- Best for: On-demand payments without maintaining balances - -## Executing a Quote - -For a prefunded quote or one from a pullable external account source, once a quote is created, execute it before it expires: - -```bash -curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes/Quote:abc123/execute \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" -``` - -**Response:** - -```json -{ - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000060", - "status": "PENDING", - "type": "OUTGOING", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000050" - ... -} -``` - -### Execution Timing - - - - - Transaction created with status `PENDING` - - Funds debited from source account immediately - - Settlement begins right away - - - - - Quote waits for payment receipt - - Once Grid receives payment with correct reference - - Quote executes automatically - - Transaction created and settlement begins - - - -## Immediate Execution - -For **market rate execution** without quote approval, use the `immediatelyExecute` flag: - -```bash Immediate quote execution -curl -X POST https://api.lightspark.com/grid/2025-10-13/quotes \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "source": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "destination": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR"}, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000, - "immediatelyExecute": true - }' -``` - - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020", - "status": "COMPLETED", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "EUR", - "name": "Euro", - "symbol": "€", - "decimals": 2 - }, - "totalSendingAmount": 100000, - "totalReceivingAmount": 91540, - "exchangeRate": 0.92, - "feesIncluded": 500, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025" -} -``` - - -- Quote is created and executed in one API call -- Best for: Rewards distribution, micro-payments, time-sensitive transfers - - -Customer doesn't see rate before execution. If you want to lock a quote and confirm fees and exchange rate details before executing the quote, set `immediatelyExecute` to `false` or omit the field. - - -## Creating External Accounts in Quotes - -You can create an external account inline when creating a quote: - -```json -{ - "source": { - "accountId": "InternalAccount:abc123" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "EUR", - "accountInfo": { - "accountType": "IBAN_ACCOUNT", - "iban": "DE89370400440532013000", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Alice Smith" - } - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 -} -``` - -This is useful for one-time recipients or when you don't want to manage external accounts separately. - -## Fees - -All fees are transparently displayed in the quote response: - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020", - "feesIncluded": 500, - ... quote response ... - "rateDetails": { - "counterpartyMultiplier": 1.08, - "counterpartyFixedFee": 10, - "gridApiMultiplier": 0.925, - "gridApiFixedFee": 10, - "gridApiVariableFeeRate": 0.003, - "gridApiVariableFeeAmount": 30 - } -} -``` - -**Rate Details Breakdown:** -- **`counterpartyMultiplier`**: Exchange rate from mSATs to receiving currency (1.08 = 1 mSAT = 1.08 cents EUR) -- **`counterpartyFixedFee`**: Fixed fee charged by counterparty (10 cents EUR) -- **`gridApiMultiplier`**: Exchange rate from sending currency to mSATs including variable fees (0.925 = $1 USD = 0.925 mSATs) -- **`gridApiFixedFee`**: Fixed fee charged by Grid API (10 cents USD) -- **`gridApiVariableFeeRate`**: Variable fee rate as percentage (0.003 = 0.3%) -- **`gridApiVariableFeeAmount`**: Variable fee amount (30 cents USD for $1,000 transaction) - -Fees are deducted from the sending amount, so: -- **Customer sends**: $1,000 -- **Fees**: $5.00 -- **Amount converted**: $995.00 -- **Recipient receives**: €915.40 (at 0.92 rate) - -## Best Practices - - - - Let customers review the exchange rate, fees, and final amounts before committing: - - ```javascript - // ✅ Good: Show quote details, await confirmation - const quote = await createQuote(params); - showQuoteToUser(quote); - if (await userConfirms()) { - await executeQuote(quote.id); - } - - // ❌ Bad: Immediate execution without review (unless micro-payments/rewards) - await createQuote({...params, immediatelyExecute: true}); - ``` - - - - Store quote parameters so you can recreate expired quotes: - - ```javascript - const quoteParams = { - source: {accountId: customerAccount}, - destination: {accountId: recipientAccount}, - lockedCurrencySide: 'SENDING', - lockedCurrencyAmount: amount - }; - - let quote = await createQuote(quoteParams); - - // Later, if expired... - try { - await executeQuote(quote.id); - } catch (error) { - if (error.code === 'QUOTE_EXPIRED') { - quote = await createQuote(quoteParams); // Recreate with fresh rate - await executeQuote(quote.id); - } - } - ``` - - - - Subscribe to quote-related webhooks: - - ```javascript - app.post('/webhooks/grid', (req, res) => { - const {transaction, type} = req.body; - - if (type === 'OUTGOING_PAYMENT' && transaction.quoteId) { - // Quote was executed, transaction created - updateCustomerUI(transaction); - } - - res.status(200).send(); - }); - ``` - - diff --git a/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx b/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx deleted file mode 100644 index 13769d72..00000000 --- a/mintlify/platform-overview/core-concepts/transaction-lifecycle.mdx +++ /dev/null @@ -1,334 +0,0 @@ ---- -title: "Transaction Lifecycle" -description: "Follow a payment from creation to settlement" ---- - -Understanding the transaction lifecycle helps you build robust payment flows, handle edge cases, and provide accurate status updates to your customers. - -## Transaction States - -Transactions progress through several states from creation to completion: - -``` -PENDING → PROCESSING → COMPLETED - ↓ - FAILED -``` - -### Status Descriptions - -| Status | Description | Next State | Actions Available | -|--------|-------------|------------|-------------------| -| **PENDING** | Transaction created, awaiting processing | PROCESSING, FAILED | Monitor status | -| **PROCESSING** | Payment being routed and settled | COMPLETED, FAILED | Monitor status | -| **COMPLETED** | Successfully delivered to recipient | Terminal | None (final state) | -| **FAILED** | Transaction failed, funds refunded if applicable | Terminal | Create new transaction | -| **REJECTED** | Rejected by recipient or compliance | Terminal | None (final state) | -| **REFUNDED** | Completed transaction later refunded | Terminal | None (final state) | -| **EXPIRED** | Quote or payment expired before execution | Terminal | Create new quote | - - -**Terminal statuses**: `COMPLETED`, `REJECTED`, `FAILED`, `REFUNDED`, `EXPIRED` - -Once a transaction reaches a terminal status, it will not change further. - - -## Outgoing Transaction Flow - -**Your customer/platform sends funds to an external recipient.** - -### Step-by-Step - - - - Lock in exchange rate and fees: - - ```bash - POST /quotes - - { - "source": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"},s - "destination": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR"}, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100000 - } - ``` - - **Response:** - - Quote ID - - Locked exchange rate - - Expiration time (1-5 minutes) - - - - Initiate the payment: - - ```bash - POST /quotes/{quoteId}/execute - ``` - - **Result:** - - Transaction created with status `PENDING` - - Source account debited immediately - - `OUTGOING_PAYMENT` webhook sent - - - - Grid handles: - - Currency conversion (if applicable) - - Routing to appropriate payment rail - - Settlement with destination bank/wallet - - **Status**: `PROCESSING` - - **Webhook**: `OUTGOING_PAYMENT` with updated status - - - - **Success Path:** - - Funds delivered to recipient - - Status: `COMPLETED` - - `settledAt` timestamp populated - - Final `OUTGOING_PAYMENT` webhook sent - - **Failure Path:** - - Delivery failed (invalid account, etc.) - - Status: `FAILED` - - `failureReason` populated - - Funds automatically refunded to source account - - Final `OUTGOING_PAYMENT` webhook sent - - - -Most transactions on Grid are completed in seconds. - -### Webhook Payloads - -**On Creation (PENDING):** - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:...", - "status": "PENDING", - "type": "OUTGOING", - "sentAmount": {"amount": 100000, "currency": {"code": "USD"}}, - "receivedAmount": {"amount": 92000, "currency": {"code": "EUR"}}, - "createdAt": "2025-10-03T15:00:00Z" - } -} -``` - -**On Completion:** - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:...", - "status": "COMPLETED", - "settledAt": "2025-10-03T15:05:00Z" - } -} -``` - -**On Failure:** - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:...", - "status": "FAILED", - "failureReason": "INVALID_BANK_ACCOUNT" - } -} -``` - -## Same-Currency Transfers - -For same-currency transfers without quotes: - -### Transfer-Out (Internal → External) - -```bash -POST /transfer-out - -{ - "source": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "destination": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "USD"}, - "amount": 100000 -} -``` - -**Response:** - -```json -{ - "id": "Transaction:...", - "status": "PENDING", - "type": "OUTGOING" -} -``` - -Follows same lifecycle as quote-based outgoing transactions. - -### Transfer-In (External → Internal) - -```bash -POST /transfer-in - -{ - "source": {"accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "USD"}, - "destination": {"accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"}, - "amount": 100000 -} -``` - -Only works for "pullable" external accounts (Plaid-linked, debit cards, etc.). - -## Monitoring Transactions - -### Via Webhooks (Recommended) - -Subscribe to transaction webhooks for real-time updates: - -```javascript -app.post('/webhooks/grid', async (req, res) => { - const {transaction, type} = req.body; - - if (type === 'OUTGOING_PAYMENT') { - await updateTransactionStatus(transaction.id, transaction.status); - - if (transaction.status === 'COMPLETED') { - await notifyCustomer(transaction.customerId, 'Payment delivered!'); - } else if (transaction.status === 'FAILED') { - await notifyCustomer(transaction.customerId, `Payment failed: ${transaction.failureReason}`); - } - } - - res.status(200).json({received: true}); -}); -``` - -### Via Polling (Backup) - -Query transaction status periodically: - -```bash -GET /transactions/{transactionId} -``` - -**Response:** - -```json -{ - "id": "Transaction:...", - "status": "PROCESSING", - "createdAt": "2025-10-03T15:00:00Z", - "updatedAt": "2025-10-03T15:02:00Z" -} -``` - -Poll every 5-10 seconds until terminal status reached. - -### Listing Transactions - -Query all transactions for a customer or date range: - -```bash -GET /transactions?customerId=Customer:abc123&startDate=2025-10-01T00:00:00Z&limit=50 -``` - -**Response:** - -```json -{ - "data": [ - { - "id": "Transaction:...", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": {"amount": 100000, "currency": {"code": "USD"}}, - "receivedAmount": {"amount": 92000, "currency": {"code": "EUR"}}, - "settledAt": "2025-10-03T15:05:00Z" - } - ], - "hasMore": false, - "nextCursor": null -} -``` - -Use for reconciliation and reporting. - -## Failure Handling - -### Common Failure Reasons - -| Failure Reason | Description | Recovery | -|----------------|-------------|----------| -| `QUOTE_EXPIRED` | Quote expired before execution | Create new quote | -| `QUOTE_EXECUTION_FAILED` | Error executing the quote | Create new quote | -| `INSUFFICIENT_BALANCE` | Source account lacks funds | Fund account, retry | -| `TIMEOUT` | Transaction timed out | Retry or contact support | - -## Best Practices - - - - Don't rely solely on polling: - - ```javascript - // ✅ Good: Webhook-driven updates - app.post('/webhooks/grid', async (req, res) => { - await handleTransactionUpdate(req.body.transaction); - res.status(200).send(); - }); - - // ❌ Bad: Constant polling - setInterval(() => getTransaction(txId), 5000); - ``` - - - - Save transaction IDs to your database: - - ```javascript - const transaction = await executeQuote(quoteId); - await db.transactions.insert({ - gridTransactionId: transaction.id, - internalPaymentId: paymentId, - status: transaction.status, - createdAt: new Date() - }); - ``` - - - - Use idempotency keys for safe retries: - - ```javascript - const idempotencyKey = `payment-${userId}-${Date.now()}`; - await createQuote({...params, idempotencyKey}); - ``` - - - - Translate technical statuses to user-friendly messages: - - ```javascript - function getUserMessage(status, failureReason) { - if (status === 'PENDING') return 'Payment processing...'; - if (status === 'PROCESSING') return 'Payment in progress...'; - if (status === 'COMPLETED') return 'Payment delivered!'; - if (status === 'FAILED') { - if (failureReason === 'INSUFFICIENT_BALANCE') { - return 'Insufficient funds. Please add money and try again.'; - } - return 'Payment failed. Please try again or contact support.'; - } - } - ``` - - diff --git a/mintlify/platform-overview/introduction/faq.mdx b/mintlify/platform-overview/introduction/faq.mdx deleted file mode 100644 index b67eeb8f..00000000 --- a/mintlify/platform-overview/introduction/faq.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: "FAQ" ---- - -## Getting Started - - - - Grid is a low-level payment infrastructure API that enables businesses, financial institutions, and developers to send and receive payments globally across fiat currencies, stablecoins, and Bitcoin. - - Grid is ideal for: - - Fintech companies building payment products - - Businesses sending international payouts - - Platforms enabling P2P payments - - Companies offering crypto on/off-ramps - - Businesses distributing rewards or incentives - - - - - - Reach out to [sales@lightspark.com](mailto:sales@lightspark.com) to discuss your use case and get custom pricing. - - - You'll receive sandbox API credentials to start building and testing your integration. - - - - Once testing is complete, you'll receive production credentials to launch with real transactions. - - - - - - No. Grid handles all on-chain operations, wallet management, and crypto conversions behind the scenes. You interact with a simple REST API using familiar concepts like customers, accounts, and transactions. Bitcoin and stablecoins are just another payment rail that Grid manages for you automatically. - - - diff --git a/mintlify/platform-overview/introduction/platform-capabilities.mdx b/mintlify/platform-overview/introduction/platform-capabilities.mdx deleted file mode 100644 index 361f18b4..00000000 --- a/mintlify/platform-overview/introduction/platform-capabilities.mdx +++ /dev/null @@ -1,230 +0,0 @@ ---- -title: "Platform Capabilities" ---- - -The Grid API enables a wide range of payment and financial services capabilities. Whether you're building a neobank, remittance service, rewards platform, or B2B payment solution, Grid provides the infrastructure you need. - -## Payment Capabilities - -### Cross-Border Payments - -Send payments across 50+ countries with automatic currency conversion and routing. Grid handles FX conversion, selects the optimal payment rail, and settles funds in the recipient's local currency. - -**Key Features:** -- Locked exchange rates valid for 1-5 minutes -- Support for both prefunded and just-in-time funding models -- Automatic rail selection (ACH, SEPA, PIX, Faster Payments, etc.) -- Real-time status tracking via webhooks -- Transparent fee structure - -**Common Use Cases:** -- International payroll -- Vendor payments -- Cross-border marketplaces -- Freelancer payments - -### Same-Currency Transfers - -Move money between accounts in the same currency without currency conversion. Ideal for domestic transfers, account funding, and withdrawals. - -**Key Features:** -- Instant transfers between internal accounts -- Push/pull to external bank accounts -- No FX fees -- Simple API with no quote required - -**Common Use Cases:** -- Wallet funding -- Withdrawals to bank accounts -- Internal ledger transfers -- Account-to-account moves - -### Fiat-to-Crypto Conversion - -Convert fiat currency to Bitcoin or stablecoins with instant delivery to self-custody wallets or Grid-managed accounts. - -**Key Features:** -- On-ramp: Bank account → Crypto -- Support for BTC, USDC, USDT, and more -- Lightning Network for instant Bitcoin delivery -- Self-custody wallet support (Spark, Solana, Tron, Base, Polygon, etc.) -- Just-in-time funding with payment instructions - -**Common Use Cases:** -- Crypto purchase platforms -- DeFi on-ramps -- Bitcoin rewards -- Trading platform funding - -### Crypto-to-Fiat Conversion - -Convert Bitcoin or stablecoins to fiat and deliver to bank accounts worldwide. - -**Key Features:** -- Off-ramp: Crypto → Bank account -- Prefunded model from customer internal accounts -- Lightning Network for instant Bitcoin receipt -- Automatic conversion and delivery to local banking rails - -**Common Use Cases:** -- Crypto cashout services -- DeFi off-ramps -- Merchant settlement in fiat -- Portfolio liquidation - -## Customer Management - -### Onboarding - -Grid supports two customer onboarding models: - - - - For regulated financial institutions that perform their own KYC/KYB: - - - Create customers directly via API - - Provide customer information you've already collected - - Instant customer approval - - You maintain compliance responsibility - - - For platforms without financial services licenses: - - - Grid provides hosted KYC/KYB flows - - Customers complete verification via Grid-hosted pages - - Grid handles compliance screening - - Webhook notification on approval/rejection - - - -Both models support: -- Individual and business customers -- UMA address assignment -- Multiple currencies per customer -- Customer-level account management - -### Account Linking - -Connect customer bank accounts and crypto wallets for funding and withdrawals: - -**Bank Account Linking:** -- Plaid integration for instant bank account connection (US) -- Manual account entry with beneficiary verification -- Support for checking and savings accounts -- Multi-currency account support - -**Crypto Wallet Linking:** -- Spark wallet addresses -- Lightning Network addresses -- Layer 1 Bitcoin addresses -- Stablecoin wallet addresses - -## UMA-Based Payments - -Grid fully implements the UMA protocol for human-readable payment addresses and cross-platform interoperability. - -### Sending UMA Payments - -Look up recipient UMA addresses and send payments across different platforms: - -1. **Receiver lookup**: Resolve `$alice@otherplatform.com` to get payment details -2. **Required fields**: Grid returns what information is required (name, address, etc.) -3. **Compliance exchange**: Send sender information for recipient platform screening -4. **Quote & execute**: Get locked rate and execute payment - -### Receiving UMA Payments - -Expose your customers' UMA addresses to receive payments from any UMA-compatible platform: - -1. **Webhook notification**: Receive `INCOMING_PAYMENT` webhook with sender info -2. **Compliance review**: Review counterparty information automatically or manually -3. **Approve/reject**: Respond with approval or rejection within 5 seconds -4. **Automatic settlement**: Funds delivered to customer account in their preferred currency - -### Invitation System - -Acquire new customers through pay-by-link invitations: - -- Create invitation links with pre-filled payment amounts -- Share via email, SMS, or social media -- Customers sign up and receive payment instantly -- Track invitation claims via webhooks - -## Reconciliation & Reporting - -### Webhook-Based Reconciliation - -Receive real-time notifications for all payment state changes: - -- **Transaction status updates**: PENDING → COMPLETED or FAILED -- **Internal account funding**: ACH deposits, wire receipts, crypto deposits -- **External account status**: Account verification, screening results -- **KYC status changes**: Customer approval/rejection - -### Query-Based Reconciliation - -Poll for transaction status when needed: - -- List transactions with filters (customer, date range, status) -- Get detailed transaction information -- Query internal account balances -- Review external account states - -### Idempotency - -All critical operations support idempotency keys for safe retries: - -- Create customers, quotes, and transactions with unique IDs -- Safely retry failed requests without duplicates -- Reconcile by checking idempotency key matches - -## Testing & Development - -### Sandbox Environment - -Full-featured sandbox environment for development: - -- All API endpoints work identically to production -- Instant settlement simulation -- Test scenarios via special account patterns -- Webhook testing and debugging -- No real money movement - -### Special Test Patterns - -Control sandbox behavior with magic account numbers: - -- Account ending in `002`: Insufficient funds -- Account ending in `003`: Account closed/invalid -- Account ending in `004`: Transfer rejected -- Account ending in `005`: Timeout (pending → failed after 30s) - -### Developer Tools - -- **Postman collection**: Pre-built API requests -- **UMA test wallet**: Test UMA payment flows -- **Dashboard logs**: Detailed request/response inspection -- **Webhook replay**: Retry webhook delivery for testing - -## Enterprise Features - -### High Availability - -- 99.9% uptime SLA -- Redundant infrastructure across multiple regions -- Automatic failover -- Rate limiting with burst tolerance - -### Scalability - -- Handle thousands of transactions per second -- Batch operations support -- Async webhook processing -- Optimized for high-volume use cases - -### White-Label Support - -- Custom domain configuration for UMA addresses -- Branded KYC flows (coming soon) -- Custom webhook URLs per environment -- API-first design for full integration control diff --git a/mintlify/platform-overview/introduction/what-is-grid.mdx b/mintlify/platform-overview/introduction/what-is-grid.mdx deleted file mode 100644 index 54780a24..00000000 --- a/mintlify/platform-overview/introduction/what-is-grid.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: "What is Grid?" ---- - -Grid is a low-level payment infrastructure API that enables modern financial institutions, businesses, or any developer to send, receive, and settle value globally across fiat currencies, stablecoins, and Bitcoin. With a single, simple API, you can build applications that move money instantly across borders without the complexity of orchestrating multiple payment rails or currencies. - -## Features - -| | -| -------------------------------------------------------------------------------------------------------------------------------------------- | -| **One API**
Move money anywhere. Fiat to fiat, crypto to fiat, or the other way around, all through a single API. | -| **Global Coverage**
Send and receive across 65+ countries using local instant rails or global crypto rails. | -| **Real-Time Settlement**
Instant, 24/7/365. Powered by Bitcoin and local payment networks. | -| **No Crypto Headaches**
We handle all the on-chain logic, wallet ops, and conversions so you don’t have to. | - -## What to build - -Grid's APIs are intentionally low-level, giving developers, institutions, and businesses full control to build any payment flow. Fiat, crypto, or both. - -Whether you're receiving Bitcoin or sending dollars as pesos to a bank account, the APIs stay completely unopinionated; you decide the flow. Each path has its own nuances, and our team can help you design the best one for your use case. - - - - - **Remittances**: Move money across countries in local currencies using the best available rails and FX routes. - - **B2B Payments**: Pay vendors or partners abroad with real-time settlement and transparent fees. - - **Payroll**: Send global salaries or contractor payouts with locked exchange rates. - - **Marketplace Payouts**: Distribute earnings to sellers or service providers worldwide in seconds. - - - - - - **Stablecoin Payouts**: Send a stablecoin to users, wallets, or bank - accounts directly. - **Global USD Account**: Let users hold a stable USD - balance anywhere in the world. - **Orchestration**: Convert liquidity between - stablecoins and fiat automatically. - **Treasury**: Hold business reserves in - stablecoins with instant conversion to local currency when needed. - - - - - **Buy & Sell Bitcoin**: Enable users to buy or sell Bitcoin instantly inside - your app. - **Rewards**: Offer instant Bitcoin cashback for actions or - purchases. - **Merchant Settlement**: Accept Bitcoin and auto-convert to fiat - on the fly. - - - - -## Next steps - - - - Understand Grid's core data model and how entities relate to each other - - - - Learn how exchange rates, pricing, and payment execution work - - - - Explore internal and external accounts and how they work together - - - - Follow a payment from creation through settlement - - - - View supported currencies, countries, and payment methods - - diff --git a/mintlify/ramps/accounts/depositing-funds.mdx b/mintlify/ramps/accounts/depositing-funds.mdx deleted file mode 100644 index d7fde2ac..00000000 --- a/mintlify/ramps/accounts/depositing-funds.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: "Depositing Funds" -description: "Depositing funds into internal accounts" ---- - -import DepositingFunds from '/snippets/depositing-funds.mdx'; - - diff --git a/mintlify/ramps/accounts/external-accounts.mdx b/mintlify/ramps/accounts/external-accounts.mdx deleted file mode 100644 index 98d54bb0..00000000 --- a/mintlify/ramps/accounts/external-accounts.mdx +++ /dev/null @@ -1,354 +0,0 @@ ---- -title: "External Accounts" -description: "Configure external bank accounts and crypto wallets for ramp destinations" ---- - -import ExternalAccountsContent from "/snippets/external-accounts.mdx"; - - - -## Using external accounts for ramps - -External accounts serve as destinations for ramp conversions: - -### For on-ramps (Fiat → Crypto) - -External accounts represent crypto wallet destinations: - -- **Spark wallets**: Lightning Network wallets for instant Bitcoin delivery -- **Self-custody**: User-controlled wallets for full ownership -- **No beneficiary required**: Crypto wallets don't need compliance information - - - Spark wallets are the recommended destination for on-ramps due to instant - settlement and minimal fees. - - -### For off-ramps (Crypto → Fiat) - -External accounts represent bank account destinations: - -- **Traditional bank accounts**: ACH, wire, SEPA, CLABE, PIX, UPI, etc. -- **Beneficiary required**: Full compliance information needed for fiat destinations -- **Multiple currencies**: Support for USD, EUR, MXN, BRL, INR, and more - - - Off-ramp destinations require complete beneficiary information for compliance. - Ensure all required fields are provided. - - -## Crypto wallet destinations - -### Spark wallet addresses - -The primary destination type for on-ramps: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "platformAccountId": "user_wallet_001", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - }' -``` - -**Response:** - -```json -{ - "id": "ExternalAccount:wallet001", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "status": "ACTIVE", - "currency": "BTC", - "platformAccountId": "user_wallet_001", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - }, - "createdAt": "2025-10-03T14:00:00Z" -} -``` - - - Spark wallet external accounts are immediately `ACTIVE` and ready for on-ramp - conversions. - - -### Validate Spark addresses - -Before creating external accounts, validate Spark wallet addresses: - -```javascript -function isValidSparkAddress(address) { - // Spark addresses start with 'spark1' and are 87 characters - return address.startsWith("spark1") && address.length === 87; -} - -const walletAddress = - "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"; - -if (!isValidSparkAddress(walletAddress)) { - throw new Error("Invalid Spark wallet address format"); -} - -// Create external account -await createExternalAccount({ - customerId, - currency: "BTC", - accountInfo: { - accountType: "SPARK_WALLET", - address: walletAddress, - }, -}); -``` - - - Spark addresses are case-insensitive and follow the bech32 format starting - with `spark1`. - - -## Bank account destinations - -For off-ramp flows, create external bank accounts with full beneficiary information: - -### Example: US bank account for off-ramp - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD", - "platformAccountId": "user_bank_usd_001", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "bankName": "Chase Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "John Doe", - "birthDate": "1990-01-15", - "nationality": "US", - "address": { - "line1": "123 Main Street", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } - }' -``` - -## Creating accounts inline with quotes - -For one-time conversions, create external accounts inline using `externalAccountDetails`: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000 - }' -``` - - - Use `externalAccountDetails` for one-time destinations. The external account - will be automatically created and can be reused for future quotes using its - returned ID. - - -## Listing external accounts - -### List customer external accounts - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -### Filter by currency - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=BTC' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -### Filter by account type - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001&accountType=SPARK_WALLET' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Account status and verification - -External accounts move through verification states: - -| Status | Description | Can Use for Conversions | -| ---------- | ------------------------ | ----------------------- | -| `PENDING` | Verification in progress | ❌ | -| `ACTIVE` | Verified and ready | ✅ | -| `FAILED` | Verification failed | ❌ | -| `DISABLED` | Manually disabled | ❌ | - - - Spark wallet accounts are immediately `ACTIVE`. Bank accounts may require - verification (typically instant to a few hours). - - -## Best practices for ramps - - - -Always validate wallet addresses and bank account details before creating external accounts: - -```javascript -// Validate Spark address format -function validateSparkAddress(address) { - if (!address.startsWith("spark1")) { - throw new Error("Spark address must start with spark1"); - } - if (address.length !== 87) { - throw new Error("Spark address must be 87 characters"); - } - // Additional validation logic - return true; -} -``` - - - - -Map external accounts to your internal system using `platformAccountId`: - -```javascript -const externalAccount = await createExternalAccount({ - customerId, - currency: "BTC", - platformAccountId: `${userId}_spark_primary`, // Your internal ID - accountInfo: { - accountType: "SPARK_WALLET", - address: sparkAddress, - }, -}); - -// Store mapping in your database -await db.userWallets.create({ - userId, - gridAccountId: externalAccount.id, - internalId: `${userId}_spark_primary`, -}); -``` - - - - -Support multiple wallets or bank accounts for flexibility: - -```javascript -// Primary Spark wallet for on-ramps -await createExternalAccount({ - customerId, - currency: "BTC", - platformAccountId: `${userId}_spark_primary`, - accountInfo: { accountType: "SPARK_WALLET", address: primaryWallet }, -}); - -// Secondary wallet for larger amounts -await createExternalAccount({ - customerId, - currency: "BTC", - platformAccountId: `${userId}_spark_savings`, - accountInfo: { accountType: "SPARK_WALLET", address: savingsWallet }, -}); -``` - - - - -For crypto destinations, implement additional verification: - -```javascript -// Verify Spark wallet is reachable (optional) -async function verifySparkWallet(address) { - try { - // Use Lightning Network tools to verify wallet exists - const probe = await lightningClient.probeWallet(address); - return probe.reachable; - } catch (error) { - console.error("Wallet verification failed:", error); - return false; - } -} - -// Only create external account after verification -if (await verifySparkWallet(sparkAddress)) { - await createExternalAccount({ - /* ... */ - }); -} -``` - - - - -## Ramp-specific considerations - -### On-ramp destinations - -- **Instant delivery**: Spark wallets receive Bitcoin within seconds -- **No KYC required**: Self-custody wallets don't need beneficiary info -- **Reusable addresses**: Store and reuse Spark addresses for multiple conversions -- **No minimum**: Send any amount supported by Lightning Network - -### Off-ramp destinations - -- **Full compliance**: Bank accounts require complete beneficiary information -- **Verification delays**: Bank account verification may take a few hours -- **Settlement times**: Vary by destination (instant for RTP/PIX, 1-3 days for ACH) -- **Amount limits**: Check minimum and maximum amounts per destination currency - - - Always verify account details before initiating large off-ramp conversions. - Test with small amounts first. - - -## Next steps - -- [Plaid Integration](/ramps/accounts/plaid) - Connect bank accounts via Plaid -- [Fiat-to-Crypto Conversion](/ramps/conversion-flows/fiat-crypto-conversion) - Build conversion flows -- [Self-Custody Wallets](/ramps/conversion-flows/self-custody-wallets) - Advanced wallet integration -- [Webhooks](/ramps/platform-tools/webhooks) - Handle account status updates - -## Related resources - -- [Platform Configuration](/ramps/onboarding/platform-configuration) - Configure supported account types -- [Sandbox Testing](/ramps/platform-tools/sandbox-testing) - Test with mock accounts -- [API Reference](/api-reference) - Complete API documentation diff --git a/mintlify/ramps/accounts/internal-accounts.mdx b/mintlify/ramps/accounts/internal-accounts.mdx deleted file mode 100644 index bd56904d..00000000 --- a/mintlify/ramps/accounts/internal-accounts.mdx +++ /dev/null @@ -1,256 +0,0 @@ ---- -title: "Internal Accounts" -description: "Manage internal accounts for holding fiat and crypto balances for ramp operations" ---- - -import InternalAccountsContent from "/snippets/internal-accounts.mdx"; - - - -## Using internal accounts for ramps - -Internal accounts play a critical role in both on-ramp and off-ramp flows: - -### For on-ramps (Fiat → Crypto) - -Internal accounts are optional for on-ramp flows using just-in-time (JIT) funding: - -- **JIT funding**: Quotes provide payment instructions directly; funds flow through without requiring an internal account -- **Pre-funded model**: Deposit fiat to internal account first, then create and execute conversion quotes - - - Most on-ramp implementations use JIT funding to avoid holding customer - balances and simplify compliance. - - -### For off-ramps (Crypto → Fiat) - -Internal accounts are essential for off-ramp flows: - -1. **Deposit crypto**: Transfer Bitcoin or stablecoins to the internal account -2. **Hold balance**: Crypto balance is held securely until conversion -3. **Execute conversion**: Create and execute quote to convert crypto to fiat and send to bank account - - - Off-ramps require pre-funded internal accounts with crypto balances. Grid - supports Fiat, BTC, and Stablecoin deposits. - - -## Crypto funding for internal accounts - -For off-ramp operations, fund internal accounts with cryptocurrency: - -### Bitcoin (Lightning Network) - -Internal accounts can receive Bitcoin via Lightning Network: - -```json -{ - "fundingPaymentInstructions": [ - { - "accountType": "SPARK_WALLET", - "assetType": "BTC", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu", - "invoice": "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3s..." - } - ] -} -``` - -**Deposit methods:** - -- **Spark address**: Reusable address for multiple deposits -- **Lightning invoice**: Single-use invoice for specific amounts - - - Lightning Network transfers are typically instant and have minimal fees, - making them ideal for crypto deposits. - - -### Stablecoins - -Internal accounts can also receive stablecoins via Lightning Network or Spark: - -```json -{ - "fundingPaymentInstructions": [ - { - "accountType": "SPARK_WALLET", - "assetType": "USDB", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - ] -} -``` - - - Stablecoins like USDB are 1:1 pegged to USD, offering price stability for - off-ramp balances while maintaining instant settlement via Lightning Network. - - -## Multi-currency balances - -Internal accounts support both fiat and crypto balances: - -### Example: Account with USD and BTC - -```json -{ - "data": [ - { - "id": "InternalAccount:usd123", - "customerId": "Customer:cust001", - "balance": { - "amount": 100000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - } - }, - { - "id": "InternalAccount:btc456", - "customerId": "Customer:cust001", - "balance": { - "amount": 10000000, - "currency": { - "code": "BTC", - "name": "Bitcoin", - "symbol": "₿", - "decimals": 8 - } - } - } - ] -} -``` - - - Always check the `decimals` field when working with balances. USD uses 2 - decimals (cents), while BTC uses 8 decimals (satoshis). - - -## Monitoring account balance changes - -Subscribe to `ACCOUNT_STATUS` webhooks to receive real-time balance updates: - -```json -{ - "accountId": "InternalAccount:btc456", - "oldBalance": { - "amount": 5000000, - "currency": { "code": "BTC", "decimals": 8 } - }, - "newBalance": { - "amount": 10000000, - "currency": { "code": "BTC", "decimals": 8 } - }, - "timestamp": "2025-10-03T14:32:00Z", - "webhookId": "Webhook:webhook001", - "type": "ACCOUNT_STATUS" -} -``` - - - Balance updates are triggered by: - Incoming deposits (fiat or crypto) - Quote - executions (funds debited) - Failed transaction reversals (funds credited - back) - - -## Ramp-specific use cases - -### Pre-funding for off-ramps - -Maintain crypto balances for instant fiat conversions: - -```javascript -// Check Bitcoin balance before off-ramp -const accounts = await getInternalAccounts(customerId); -const btcAccount = accounts.data.find( - (acc) => acc.balance.currency.code === "BTC" -); - -if (btcAccount.balance.amount >= requiredSats) { - // Create quote to convert BTC to fiat - const quote = await createQuote({ - source: { accountId: btcAccount.id }, - destination: { accountId: bankAccountId, currency: "USD" }, - lockedCurrencySide: "SENDING", - lockedCurrencyAmount: requiredSats, - }); - - // Execute conversion - await executeQuote(quote.id); -} -``` - -### Platform treasury management - -Use platform-level internal accounts for pooled liquidity: - -```bash -# Get platform internal accounts -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/platform/internal-accounts?currency=BTC' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - - - Platform internal accounts enable centralized treasury management for - high-volume ramp operations. - - -## Best practices for ramps - - - -- **On-ramps**: Use JIT funding to avoid holding customer fiat -- **Off-ramps**: Pre-fund with crypto for instant fiat conversions -- **High volume**: Consider platform-level accounts for pooled liquidity - - - -```javascript -// Track BTC value in USD -const btcBalance = account.balance.amount; // satoshis -const currentRate = await getBtcUsdRate(); -const usdValue = (btcBalance / 100000000) * currentRate; - -if (usdValue > maxExposure) { -// Convert excess BTC to stablecoin -await convertToStablecoin(btcBalance - targetBalance); -} - -```` - - - -Set up proactive notifications for low balances: -```javascript -// Alert when off-ramp liquidity is low -if (btcAccount.balance.amount < minimumSats) { - await alertTreasury({ - type: 'LOW_CRYPTO_BALANCE', - account: btcAccount.id, - balance: btcAccount.balance.amount, - minimumRequired: minimumSats - }); -} -```` - - - - -## Next steps - -- [External Accounts](/ramps/accounts/external-accounts) - Configure destination accounts for conversions -- [Plaid Integration](/ramps/accounts/plaid) - Connect bank accounts via Plaid -- [Fiat-to-Crypto Conversion](/ramps/conversion-flows/fiat-crypto-conversion) - Build conversion flows -- [Webhooks](/ramps/platform-tools/webhooks) - Handle real-time notifications - -## Related resources - -- [Platform Configuration](/ramps/onboarding/platform-configuration) - Configure supported currencies -- [Sandbox Testing](/ramps/platform-tools/sandbox-testing) - Test funding and conversions safely -- [API Reference](/api-reference) - Complete API documentation diff --git a/mintlify/ramps/accounts/plaid.mdx b/mintlify/ramps/accounts/plaid.mdx deleted file mode 100644 index afbf3a7e..00000000 --- a/mintlify/ramps/accounts/plaid.mdx +++ /dev/null @@ -1,400 +0,0 @@ ---- -title: "External Accounts with Plaid" -description: "Connect bank accounts securely via Plaid for seamless off-ramp destinations" ---- - -import PlaidContent from "/snippets/plaid-integration.mdx"; - - - -## Using Plaid for ramps - -Plaid integration is particularly valuable for off-ramp flows where users convert crypto to fiat and need a bank account destination: - -### Off-ramp benefits - -- **Seamless UX**: Users authenticate with their bank directly—no manual account entry -- **Instant verification**: Bank accounts are verified in real-time -- **Reduced errors**: Eliminates typos in account numbers and routing information -- **Higher conversion**: Simplified flow increases completion rates - - - Plaid is recommended for consumer off-ramp flows where users need to quickly - connect a bank account to receive fiat currency. - - -### When to use Plaid - -Use Plaid integration when: - -- **Consumer off-ramps**: Individual users converting crypto to USD/fiat -- **First-time users**: Simplifying the initial bank account setup -- **Mobile apps**: Plaid's mobile SDKs provide native integration -- **Compliance**: Plaid's bank authentication provides additional verification - -### When to use manual entry - -Manual bank account entry may be preferred for: - -- **Business accounts**: Corporate bank accounts often require additional documentation -- **International accounts**: Plaid primarily supports US banks (though expanding) -- **Wire transfers**: Large amounts that require wire-specific account details -- **Non-supported banks**: Some smaller banks or credit unions may not be available via Plaid - -## Ramp-specific implementation - -### Off-ramp flow with Plaid - - - - User requests to convert Bitcoin to USD and wants to receive funds in their bank account. - - ```javascript - // User clicks "Cash out Bitcoin" - const initiateOffRamp = async (amountSats) => { - // Check if user has connected bank account - const hasAccount = await checkExternalBankAccount(userId); - - if (!hasAccount) { - // Trigger Plaid flow - await connectBankViaPlaid(); - } else { - // Proceed with quote - await createOffRampQuote(amountSats); - } - }; - ``` - - - - Your backend requests a link token for the customer from Grid. - - ```javascript - const response = await fetch( - 'https://api.lightspark.com/grid/2025-10-13/plaid/link-tokens', - { - method: 'POST', - headers: { - 'Authorization': `Basic ${gridCredentials}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - customerId: gridCustomerId, - }), - } - ); - - const { linkToken, callbackUrl } = await response.json(); - ``` - - - - Show Plaid Link UI to the user for bank authentication. - - ```javascript - const { open } = usePlaidLink({ - token: linkToken, - onSuccess: async (publicToken, metadata) => { - // Forward to your backend - await fetch('/api/plaid/connect-bank', { - method: 'POST', - body: JSON.stringify({ - publicToken, - accountId: metadata.account_id, - }), - }); - - // Show success message - setMessage('Bank connected! Processing your withdrawal...'); - }, - }); - ``` - - - - Grid creates the external account and sends a webhook notification. - - ```javascript - // Your webhook handler - if (webhookPayload.type === 'ACCOUNT_STATUS' && webhookPayload.account) { - const { account, customerId } = webhookPayload; - - // Bank account is ready for off-ramp - await db.users.update({ - where: { gridCustomerId: customerId }, - data: { bankAccountId: account.accountId }, - }); - - // Automatically proceed with off-ramp if amount is pending - const pendingOffRamp = await db.offRamps.findPending(customerId); - if (pendingOffRamp) { - await createOffRampQuote({ - customerId, - sourceAccountId: pendingOffRamp.btcAccountId, - destinationAccountId: account.accountId, - amountSats: pendingOffRamp.amountSats, - }); - } - } - ``` - - - -### Combining Plaid with quote creation - -For a seamless user experience, combine Plaid bank connection with immediate quote execution: - -```javascript -// Complete off-ramp flow -async function executeOffRamp({ userId, amountSats }) { - const customer = await getCustomer(userId); - - // Check for existing bank account - let bankAccountId = customer.bankAccountId; - - if (!bankAccountId) { - // Initiate Plaid flow - const plaidToken = await requestPlaidLinkToken(customer.gridCustomerId); - - // Wait for user to complete Plaid (via frontend) - await waitForPlaidCompletion(userId); - - // Refresh customer to get bank account ID - const updated = await getCustomer(userId); - bankAccountId = updated.bankAccountId; - } - - // Create off-ramp quote - const quote = await createQuote({ - source: { - accountId: customer.btcInternalAccountId, // User's BTC balance - }, - destination: { - accountId: bankAccountId, // Plaid-connected bank account - currency: "USD", - }, - lockedCurrencySide: "SENDING", - lockedCurrencyAmount: amountSats, - }); - - // Execute quote - await executeQuote(quote.id); - - return quote; -} -``` - -## Advanced patterns - -### Pre-fetching link tokens - -Improve UX by prefetching link tokens before users request off-ramps: - -```javascript -// On app load or settings page -useEffect(() => { - async function prefetchPlaidToken() { - if (!user.hasBankAccount && !sessionStorage.getItem("plaidLinkToken")) { - const response = await fetch("/api/plaid/link-token"); - const { linkToken } = await response.json(); - sessionStorage.setItem("plaidLinkToken", linkToken); - } - } - - prefetchPlaidToken(); -}, [user]); - -// When user clicks "Cash out", token is already available -const startOffRamp = () => { - const linkToken = sessionStorage.getItem("plaidLinkToken"); - if (linkToken) { - plaidLinkHandler.open(); - } -}; -``` - - - Link tokens expire after 4 hours, so prefetch close to when you expect users - to need them. - - -### Handling multiple bank accounts - -Allow users to connect multiple bank accounts for different off-ramp scenarios: - -```javascript -// List all Plaid-connected accounts -async function listBankAccounts(userId) { - const customer = await getCustomer(userId); - const accounts = await fetch( - `https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=${customer.gridCustomerId}&accountType=US_ACCOUNT`, - { headers: { Authorization: `Basic ${gridCredentials}` } } - ); - - return accounts.json(); -} - -// Let user choose destination for off-ramp -const selectBankForOffRamp = async (amountSats) => { - const banks = await listBankAccounts(userId); - - if (banks.data.length === 0) { - // No banks connected, trigger Plaid - await connectNewBank(); - } else { - // Show bank selection UI - showBankSelector(banks.data, (selectedBank) => { - createOffRampQuote({ - destinationAccountId: selectedBank.id, - amountSats, - }); - }); - } -}; -``` - -### Error recovery - -Gracefully handle Plaid errors and offer alternatives: - -```javascript -const { open } = usePlaidLink({ - token: linkToken, - onSuccess: handleSuccess, - onExit: (error, metadata) => { - if (error) { - console.error("Plaid error:", error); - - // Show error-specific messaging - if (error.error_code === "ITEM_LOGIN_REQUIRED") { - setError( - "Your bank requires you to log in again. Please try reconnecting." - ); - } else if (error.error_code === "INSTITUTION_NOT_RESPONDING") { - setError( - "Your bank is temporarily unavailable. Please try again later or connect manually." - ); - // Offer manual entry option - setShowManualEntry(true); - } else { - setError( - "Unable to connect to your bank. Would you like to enter your account details manually?" - ); - setShowManualEntry(true); - } - } else { - // User exited without completing - console.log("User closed Plaid without connecting"); - } - }, -}); - -{ - showManualEntry && ; -} -``` - -## Best practices for ramps - - - -Plaid's mobile SDKs provide the best UX for app-based off-ramps: - -```javascript -// React Native example -import { PlaidLink } from "react-native-plaid-link-sdk"; - - { - await submitToBackend(publicToken); - // Navigate to off-ramp confirmation - navigation.navigate("OffRampConfirm"); - }} -> - - Connect Bank Account - -; -``` - - - - -Store Plaid-connected accounts to avoid re-connection: - -```javascript -// After successful Plaid connection -const handlePlaidSuccess = async (account) => { - // Store reference in your DB - await db.users.update({ - where: { id: userId }, - data: { - primaryBankAccountId: account.id, - bankAccountLastFour: account.accountInfo.accountNumber.slice(-4), - bankName: account.accountInfo.bankName, - }, - }); - - // Show in UI without re-fetching - setBankAccount({ - id: account.id, - lastFour: account.accountInfo.accountNumber.slice(-4), - bankName: account.accountInfo.bankName, - }); -}; -``` - - - - -Keep users informed during async bank account creation: - -```javascript -// Show loading state while waiting for webhook -const [accountStatus, setAccountStatus] = useState("connecting"); - -useEffect(() => { - // After Plaid success - if (plaidConnected) { - setAccountStatus("verifying"); - - // Poll for account or wait for WebSocket - const pollInterval = setInterval(async () => { - const account = await checkBankAccountStatus(userId); - if (account?.status === "ACTIVE") { - setAccountStatus("ready"); - clearInterval(pollInterval); - } - }, 2000); - - return () => clearInterval(pollInterval); - } -}, [plaidConnected]); - -// Show appropriate UI -{ - accountStatus === "verifying" && ( -
- -

Verifying your bank account...

-
- ); -} -``` - -
-
- -## Next steps - -- [Fiat-to-Crypto Conversion](/ramps/conversion-flows/fiat-crypto-conversion) - Build conversion flows -- [Internal Accounts](/ramps/accounts/internal-accounts) - Manage crypto balances for off-ramps -- [Webhooks](/ramps/platform-tools/webhooks) - Handle account creation notifications -- [Sandbox Testing](/ramps/platform-tools/sandbox-testing) - Test Plaid integration safely - -## Related resources - -- [External Accounts](/ramps/accounts/external-accounts) - Manual bank account setup -- [Platform Configuration](/ramps/onboarding/platform-configuration) - Enable Plaid for your platform -- [API Reference](/api-reference) - Complete Plaid API documentation diff --git a/mintlify/ramps/conversion-flows/fiat-crypto-conversion.mdx b/mintlify/ramps/conversion-flows/fiat-crypto-conversion.mdx deleted file mode 100644 index 3dfc8340..00000000 --- a/mintlify/ramps/conversion-flows/fiat-crypto-conversion.mdx +++ /dev/null @@ -1,290 +0,0 @@ ---- -title: "Fiat-to-Crypto and Crypto-to-Fiat" -description: "Build on-ramp and off-ramp flows to convert between fiat currencies and cryptocurrencies" ---- - -## Overview - -Grid enables seamless conversion between fiat currencies and cryptocurrencies via the Lightning Network. Use quotes to lock exchange rates and get payment instructions for completing transfers. - -**On-ramp (Fiat → Crypto):** User sends fiat → Grid detects payment → Crypto sent to wallet - -**Off-ramp (Crypto → Fiat):** Execute quote → Grid processes crypto → Fiat sent to bank - -## Prerequisites - -- Customer created in Grid -- **On-ramps:** Destination crypto wallet (Spark address) + webhook endpoint -- **Off-ramps:** Internal account with crypto + external bank account registered - -## On-ramp: Fiat to crypto - -### Create a quote - - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "beneficiary": { - "counterPartyType": "INDIVIDUAL", - "fullName": "John Doe", - "email": "john@example.com" - }, - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "RECEIVING", - "lockedCurrencyAmount": 100000, - "description": "Buy 0.001 BTC" - }' -``` - -```javascript Node.js -const quote = await fetch("https://api.lightspark.com/grid/2025-10-13/quotes", { - method: "POST", - headers: { - Authorization: `Basic ${credentials}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - source: { - customerId: "Customer:019542f5-b3e7-1d02-0000-000000000001", - currency: "USD", - }, - destination: { - externalAccountDetails: { - customerId: "Customer:019542f5-b3e7-1d02-0000-000000000001", - currency: "BTC", - beneficiary: { - counterPartyType: "INDIVIDUAL", - fullName: "John Doe", - email: "john@example.com", - }, - accountInfo: { - accountType: "SPARK_WALLET", - address: - "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu", - }, - }, - }, - lockedCurrencySide: "RECEIVING", - lockedCurrencyAmount: 100000, // 0.001 BTC in satoshis - description: "Buy 0.001 BTC", - }), -}).then((r) => r.json()); -``` - - - -**Response includes payment instructions:** - -```json -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "totalSendingAmount": 6500, - "receivingAmount": 100000, - "expiresAt": "2025-10-03T12:05:00Z", - "paymentInstructions": [ - { - "accountOrWalletInfo": { - "accountType": "US_ACCOUNT", - "reference": "UMA-Q12345-REF", - "accountNumber": "1234567890", - "routingNumber": "021000021", - "bankName": "Grid Settlement Bank" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ] -} -``` - -### Display payment instructions - -```javascript -function displayPaymentInstructions(quote) { - const instructions = quote.paymentInstructions[0]; - - return { - amount: `$${(quote.totalSendingAmount / 100).toFixed(2)}`, - bankName: instructions.accountOrWalletInfo.bankName, - accountNumber: instructions.accountOrWalletInfo.accountNumber, - routingNumber: instructions.accountOrWalletInfo.routingNumber, - referenceCode: instructions.reference, // User must include this - expiresAt: quote.expiresAt, - willReceive: `${quote.receivingAmount / 100000000} BTC`, - }; -} -``` - -### Monitor completion - -Grid sends a webhook when the transfer completes: - -```javascript -app.post("/webhooks/grid", async (req, res) => { - const { type, transaction } = req.body; - - if (type === "OUTGOING_PAYMENT" && transaction.status === "COMPLETED") { - await notifyUser(transaction.customerId, { - message: "Your Bitcoin purchase is complete!", - amount: `${transaction.receivedAmount.amount / 100000000} BTC`, - }); - } - - res.status(200).json({ received: true }); -}); -``` - -## Off-ramp: Crypto to fiat - -### Create and execute a quote - -```javascript -// 1. Create quote -const quote = await fetch("https://api.lightspark.com/grid/2025-10-13/quotes", { - method: "POST", - body: JSON.stringify({ - source: { - accountId: "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - }, - destination: { - accountId: "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - currency: "USD", - }, - lockedCurrencySide: "SENDING", - lockedCurrencyAmount: 100000, // 0.001 BTC - description: "Sell 0.001 BTC", - }), -}).then((r) => r.json()); - -// 2. Execute quote -const result = await fetch( - `https://api.lightspark.com/grid/2025-10-13/quotes/${quote.id}/execute`, - { - method: "POST", - headers: { Authorization: `Basic ${credentials}` }, - } -).then((r) => r.json()); -``` - -### Track completion - -```javascript -app.post("/webhooks/grid", async (req, res) => { - const { type, transaction } = req.body; - - if (type === "OUTGOING_PAYMENT" && transaction.status === "COMPLETED") { - await notifyUser(transaction.customerId, { - message: "Your USD withdrawal is complete!", - amount: `$${transaction.receivedAmount.amount / 100}`, - }); - } - - res.status(200).json({ received: true }); -}); -``` - -## Immediate execution - -For instant on-ramps (e.g., reward payouts), use `immediatelyExecute: true`: - -```javascript -const quote = await fetch("https://api.lightspark.com/grid/2025-10-13/quotes", { - method: "POST", - body: JSON.stringify({ - source: { customerId: "Customer:...", currency: "USD" }, - destination: { - externalAccountDetails: { - /* wallet details */ - }, - }, - lockedCurrencySide: "RECEIVING", - lockedCurrencyAmount: 100000, - immediatelyExecute: true, - }), -}).then((r) => r.json()); -``` - -## Best practices - - - -```javascript -async function refreshQuoteIfNeeded(quote) { - const expiresAt = new Date(quote.expiresAt); - const now = new Date(); - - if (expiresAt - now < 60000) { - // Less than 1 minute left - return await createNewQuote(quote.originalParams); - } - - return quote; -} -``` - - - -```javascript -const settlementTimes = { - US_ACCOUNT: "1-3 business days (ACH)", - WIRE: "Same day", - SPARK_WALLET: "Instant", - PIX: "Instant", - SEPA: "1-2 business days", -}; -``` - - - -```javascript -if (transaction.status === "FAILED") { - await notifyUser(transaction.customerId, { - message: "Transaction failed", - reason: transaction.failureReason, - action: "retry", - }); - - if (transaction.failureReason === "QUOTE_EXPIRED") { - await createNewQuote(transaction.originalParams); - } -} -``` - - - -## Next steps - - - - Send crypto to user-controlled wallets - - - - Monitor transaction status - - - - Complete API documentation - - diff --git a/mintlify/ramps/conversion-flows/self-custody-wallets.mdx b/mintlify/ramps/conversion-flows/self-custody-wallets.mdx deleted file mode 100644 index a09f0c84..00000000 --- a/mintlify/ramps/conversion-flows/self-custody-wallets.mdx +++ /dev/null @@ -1,432 +0,0 @@ ---- -title: "Self-Custody Wallet Integration" -description: "Send and receive cryptocurrency to and from user-controlled wallets" ---- - -## Overview - -Grid supports sending Bitcoin via Lightning Network to self-custody wallets using Spark wallet addresses. This enables users to maintain full control of their crypto while benefiting from Grid's fiat-to-crypto conversion and payment rails. - - - Spark wallets use the Lightning Network for instant, low-cost Bitcoin - transactions. Users can receive payments directly to their self-custody - wallets without Grid holding their funds. - - -## How it works - -1. **User provides wallet address** - Get Spark wallet address from user -2. **Create quote** - Generate quote for fiat-to-crypto or crypto-to-crypto transfer -3. **Execute transfer** - Send crypto directly to user's wallet -4. **Instant settlement** - Lightning Network provides near-instant confirmation - -## Prerequisites - -- Customer created in Grid -- Valid Spark wallet address from user -- Webhook endpoint for payment notifications (optional, for status updates) - -## Sending crypto to self-custody wallets - -### Step 1: Collect wallet address - -Request the user's Spark wallet address. Spark addresses start with `spark1`: - -```javascript -function validateSparkAddress(address) { - // Spark addresses start with spark1 and are typically 90+ characters - if (!address.startsWith("spark1")) { - throw new Error("Invalid Spark wallet address"); - } - - if (address.length < 90) { - throw new Error("Spark address appears incomplete"); - } - - return address; -} - -// Example valid address -const sparkAddress = - "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"; -``` - - - Always validate wallet addresses before creating quotes. Invalid addresses - will cause transaction failures and potential fund loss. - - -### Step 2: Create external account for the wallet - -Register the Spark wallet as an external account: - - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "beneficiary": { - "counterPartyType": "INDIVIDUAL", - "fullName": "John Doe", - "email": "john@example.com" - }, - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - }' -``` - -```javascript Node.js -const externalAccount = await fetch( - "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts", - { - method: "POST", - headers: { - Authorization: `Basic ${credentials}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - customerId: "Customer:019542f5-b3e7-1d02-0000-000000000001", - currency: "BTC", - beneficiary: { - counterPartyType: "INDIVIDUAL", - fullName: "John Doe", - email: "john@example.com", - }, - accountInfo: { - accountType: "SPARK_WALLET", - address: sparkAddress, - }, - }), - } -).then((r) => r.json()); - -console.log("External account ID:", externalAccount.id); -``` - -```python Python -import requests -import base64 - -credentials = base64.b64encode( - f"{api_token_id}:{api_secret}".encode() -).decode() - -response = requests.post( - 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts', - headers={ - 'Authorization': f'Basic {credentials}', - 'Content-Type': 'application/json' - }, - json={ - 'customerId': 'Customer:019542f5-b3e7-1d02-0000-000000000001', - 'currency': 'BTC', - 'beneficiary': { - 'counterPartyType': 'INDIVIDUAL', - 'fullName': 'John Doe', - 'email': 'john@example.com' - }, - 'accountInfo': { - 'accountType': 'SPARK_WALLET', - 'address': spark_address - } - } -) - -external_account = response.json() -print(f"External account ID: {external_account['id']}") -``` - - - - - Store the external account ID for future transfers. Users can reuse the same - wallet address for multiple transactions. - - -### Step 3: Create and execute a quote - -#### Option A: From fiat to self-custody wallet - -Convert fiat directly to crypto in user's wallet: - -```javascript -// Create quote for fiat-to-crypto -const quote = await fetch("https://api.lightspark.com/grid/2025-10-13/quotes", { - method: "POST", - body: JSON.stringify({ - source: { - customerId: "Customer:019542f5-b3e7-1d02-0000-000000000001", - currency: "USD", - }, - destination: { - accountId: externalAccount.id, - currency: "BTC", - }, - lockedCurrencySide: "SENDING", - lockedCurrencyAmount: 10000, // $100.00 - description: "Buy Bitcoin to self-custody wallet", - }), -}).then((r) => r.json()); - -// Display payment instructions to user -console.log("Send fiat to:", quote.paymentInstructions); -console.log("Will receive:", `${quote.receivingAmount / 100000000} BTC`); -``` - -#### Option B: From internal account to self-custody wallet - -Transfer crypto from internal account to user's wallet: - -```javascript -// Same-currency transfer (no quote needed) -const transaction = await fetch( - "https://api.lightspark.com/grid/2025-10-13/transfer-out", - { - method: "POST", - body: JSON.stringify({ - source: { - accountId: "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - }, - destination: { - accountId: externalAccount.id, - }, - amount: 100000, // 0.001 BTC in satoshis - }), - } -).then((r) => r.json()); - -console.log("Transfer initiated:", transaction.id); -``` - -### Step 4: Monitor transfer completion - -Track the transfer status via webhooks: - -```javascript -app.post("/webhooks/grid", async (req, res) => { - const { type, transaction } = req.body; - - if (type === "OUTGOING_PAYMENT") { - if (transaction.status === "COMPLETED") { - // Notify user of successful transfer - await notifyUser(transaction.customerId, { - message: "Bitcoin sent to your wallet!", - amount: `${transaction.receivedAmount.amount / 100000000} BTC`, - walletAddress: transaction.destination.accountInfo.address, - }); - } else if (transaction.status === "FAILED") { - // Handle failure - await notifyUser(transaction.customerId, { - message: "Transfer failed", - reason: transaction.failureReason, - action: "Please verify your wallet address", - }); - } - } - - res.status(200).json({ received: true }); -}); -``` - -## Best practices - - - -Always validate Spark addresses before processing: - -```javascript -function validateSparkAddress(address) { - // Check format - if (!address.startsWith("spark1")) { - return { valid: false, error: "Must start with spark1" }; - } - - // Check length (typical Spark addresses are 90+ chars) - if (address.length < 90) { - return { valid: false, error: "Address too short" }; - } - - // Check for common typos - if (address.includes(" ") || address.includes("\n")) { - return { valid: false, error: "Address contains whitespace" }; - } - - return { valid: true }; -} -``` - - - - -Lightning Network has unique characteristics: - -```javascript -const lightningLimits = { - minAmount: 1000, // 0.00001 BTC (1000 satoshis) - maxAmount: 10000000, // 0.1 BTC (10M satoshis) - settlementTime: "Instant (typically < 10 seconds)", - fees: "Very low (typically < 1%)", -}; - -function validateLightningAmount(satoshis) { - if (satoshis < lightningLimits.minAmount) { - throw new Error(`Minimum amount is ${lightningLimits.minAmount} sats`); - } - - if (satoshis > lightningLimits.maxAmount) { - throw new Error(`Maximum amount is ${lightningLimits.maxAmount} sats`); - } - - return true; -} -``` - - - - -Help users understand the process: - -```javascript -function getWalletInstructions(walletType) { - return { - SPARK_WALLET: { - title: "Lightning Network Wallet", - steps: [ - "Open your Lightning wallet app", - 'Select "Send" or "Pay"', - "Scan the QR code or paste the address", - "Confirm the amount and send", - "Funds arrive instantly", - ], - compatibleWallets: [ - "Spark Wallet", - "Phoenix", - "Breez", - "Muun", - "Blue Wallet (Lightning)", - ], - }, - }; -} -``` - - - - -Implement retry logic for common failures: - -```javascript -async function handleTransferFailure(transaction) { - const { failureReason } = transaction; - - const retryableReasons = [ - "TEMPORARY_NETWORK_ERROR", - "INSUFFICIENT_LIQUIDITY", - "ROUTE_NOT_FOUND", - ]; - - if (retryableReasons.includes(failureReason)) { - // Retry after delay - await delay(5000); - return await retryTransfer(transaction.quoteId); - } - - // Non-retryable errors - const errorMessages = { - INVALID_ADDRESS: - "The wallet address is invalid. Please verify and try again.", - AMOUNT_TOO_SMALL: - "Amount is below minimum. Lightning Network requires at least 1000 satoshis.", - AMOUNT_TOO_LARGE: - "Amount exceeds Lightning Network limits. Consider splitting into multiple transfers.", - }; - - return { - canRetry: false, - message: - errorMessages[failureReason] || - "Transfer failed. Please contact support.", - }; -} -``` - - - - -## Testing in sandbox - -Test self-custody wallet flows in sandbox mode: - -```javascript -// Sandbox: Use test Spark addresses -const testSparkAddress = - "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"; - -// Create test transfer -const testTransfer = await fetch( - "https://api.lightspark.com/grid/2025-10-13/quotes", - { - method: "POST", - body: JSON.stringify({ - source: { accountId: "InternalAccount:..." }, - destination: { - externalAccountDetails: { - customerId: "Customer:...", - currency: "BTC", - beneficiary: { - /* test data */ - }, - accountInfo: { - accountType: "SPARK_WALLET", - address: testSparkAddress, - }, - }, - }, - lockedCurrencySide: "SENDING", - lockedCurrencyAmount: 10000, - immediatelyExecute: true, - }), - } -).then((r) => r.json()); - -// Simulate completion (sandbox auto-completes) -console.log("Test transfer status:", testTransfer.status); -``` - - - In sandbox mode, transfers to Spark wallets complete instantly without - requiring actual Lightning Network transactions. - - -## Next steps - - - - Learn about on-ramp and off-ramp flows - - - - Manage external accounts including Spark wallets - - - - Set up webhooks to monitor transaction status - - - - Test wallet integrations in sandbox mode - - diff --git a/mintlify/ramps/index.mdx b/mintlify/ramps/index.mdx deleted file mode 100644 index 60a4e120..00000000 --- a/mintlify/ramps/index.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: "Introduction" -description: "Convert between fiat and crypto with Grid's on-ramp and off-ramp infrastructure" ---- - -import { topLevelProductName } from '/snippets/variables.mdx'; -import { rampsProductName } from '/snippets/variables.mdx'; - -With Grid, you can seamlessly convert between fiat currencies and cryptocurrencies through a single, simple API. The {topLevelProductName} automatically handles currency conversion, compliance, and instant settlement via the Lightning Network. - - - - Convert fiat to crypto (on-ramp) or crypto to fiat (off-ramp) in seconds - using real-time exchange rates. - - - Grid handles the conversion and settlement process, reducing complexity in - your crypto integration. - - - Leverages the Lightning Network for instant Bitcoin transfers and local - banking rails for fiat settlement worldwide. - - - ---- - -## How Ramps Work - - - - Get real-time exchange rates and payment instructions for your desired - conversion (fiat → crypto or crypto → fiat). - - - Fund the quote using the provided payment instructions. Grid executes the - conversion at the quoted rate. - - - Receive your converted funds via Lightning Network (for crypto) or local - banking rails (for fiat) within seconds. - - - ---- - -## {rampsProductName} Features - -Users interact with {rampsProductName} through two main interfaces: - - - - Programmatic access to create quotes, fund conversions, execute transfers, - and reconcile all activity with real-time webhooks. - - - Your development and operations team can use the dashboard to monitor - conversions, manage API keys and environments, and troubleshoot with - detailed logs. - - - -### On-Ramp: Fiat to Crypto - -Convert fiat currency to cryptocurrency and deliver it to self-custody wallets. - -- **Create quotes** to lock exchange rates for fiat-to-crypto conversions -- **Payment instructions** guide users on how to fund their conversion -- **Instant delivery** to Spark wallets or other supported crypto destinations -- **Webhook notifications** confirm successful conversion and delivery - -### Off-Ramp: Crypto to Fiat - -Convert cryptocurrency to fiat currency and deliver it to bank accounts. - -- **Pre-funded accounts** hold crypto balances for conversion -- **Real-time rates** for crypto-to-fiat exchange -- **Bank account delivery** via local payment rails -- **Compliance** handled automatically through Grid's infrastructure - -### Funding Options - -{rampsProductName} supports multiple funding models for on-ramps and off-ramps: - -- **Just-in-time (JIT)**: Create a quote and fund it in real-time using payment instructions (ideal for on-ramps) -- **Pre-funded accounts**: Maintain balances in crypto or fiat and convert from those balances (ideal for off-ramps) - - - You can mix funding models based on your use case. On-ramps typically use JIT - funding, while off-ramps use pre-funded accounts. - - -### Environments - - -{rampsProductName} supports two environments: **Sandbox** and **Production**. - -The Sandbox mirrors production behavior, allowing you to test the full end-to-end flow—from creating quotes and simulating funding to executing conversions and receiving webhooks—without moving real funds. - -The Production environment uses live credentials and base URLs for real transactions once you're ready to launch. - - - ---- - - - Ready to integrate {rampsProductName}? Check out our quickstart guide. - diff --git a/mintlify/ramps/onboarding/configuring-customers.mdx b/mintlify/ramps/onboarding/configuring-customers.mdx deleted file mode 100644 index 621e55c6..00000000 --- a/mintlify/ramps/onboarding/configuring-customers.mdx +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: "Configuring Customers" -description: "Create and manage customers for ramp conversions" ---- - -import KycRegulated from '/snippets/kyc/kyc-regulated.mdx' -import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx' -import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx' - -Customers must complete identity verification before processing conversions. The required information varies based on your platform's regulatory status. - - - - - - - - - - -## Monitor verification status - - - - - Only customers with `APPROVED` status can create quotes and process - conversions. - - ---- - -## Customer types - - - -For personal conversions and consumer wallets. - -**Required fields:** `fullName`, `email`, `birthDate`, `address` - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "platformCustomerId": "user_12345", - "customerType": "INDIVIDUAL", - "fullName": "Alice Johnson", - "email": "alice@example.com", - "birthDate": "1990-01-15", - "address": {...} - }' -``` - - - - -For corporate conversions and business accounts. - -**Required fields:** `businessName`, `email`, `taxId`, `address` - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "platformCustomerId": "biz_67890", - "customerType": "BUSINESS", - "businessName": "Acme Corporation", - "email": "finance@acme.com", - "taxId": "12-3456789", - "address": {...} - }' -``` - - - - ---- - -## Next steps - - - -Fund crypto for off-ramp conversions - - - - Set up wallet destinations - - - - Build conversion flows - - - -Complete customer API docs - - diff --git a/mintlify/ramps/onboarding/implementation-overview.mdx b/mintlify/ramps/onboarding/implementation-overview.mdx deleted file mode 100644 index 94a47d01..00000000 --- a/mintlify/ramps/onboarding/implementation-overview.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: "Implementation Overview" ---- - -import { rampsProductName } from '/snippets/variables.mdx'; - -This page gives you a 10,000‑ft view of an end‑to‑end Grid implementation for on and off-ramps. -It is intentionally generalized to cover both on-ramp (fiat → crypto) and off-ramp (crypto → fiat) flows. -The detailed guides that follow provide concrete fields, edge cases, and step‑by‑step instructions. - - - This overview highlights the main building blocks: platform setup, customer - onboarding, account management, conversion flows, reconciliation, sandbox - testing, and go‑live enablement. - - -## Platform configuration - -Configure your platform once before building user flows. - -- Provide webhook endpoints for transaction and conversion status notifications -- Generate API credentials for Sandbox (and later Production) -- Configure supported currencies for conversions (fiat and crypto) -- Review settlement methods and supported crypto destinations - -## Onboarding customers - -Onboard customers who will use ramp services. There are two patterns: - -- Regulated platforms can directly create customers by providing minimal KYC data via API -- Unregulated entities should request a KYC link and embed the hosted KYC flow; once completed, the customer can transact - -You'll need to persist the Grid customer IDs for use in conversion flows. - -## Account management - -Set up accounts for funding and receiving conversions. - -### Internal Accounts - -- Platform and customer internal accounts hold fiat or crypto balances -- Used for pre-funded off-ramp scenarios (crypto → fiat) -- Fund via ACH, wire, or crypto deposits - -### External Accounts - -- Register crypto wallets (Spark, Bitcoin addresses) for on-ramp destinations -- Register bank accounts for off-ramp destinations -- Capture required beneficiary and account information - - - On-ramps typically deliver to external crypto wallets, while off-ramps - typically deliver to external bank accounts. - - -## On-ramp flow (Fiat → Crypto) - -Enable users to convert fiat currency to cryptocurrency. - -### Quote Creation - -- Create a quote specifying source (fiat) and destination (crypto wallet) -- Lock exchange rate and receive payment instructions -- Quote includes reference code for payment matching - -### Just-in-Time Funding - -- User sends fiat payment to provided bank account details -- Include reference code in payment memo for matching -- Grid detects payment and automatically executes conversion - -### Crypto Delivery - -- Grid converts fiat to crypto at locked rate -- Crypto delivered to specified wallet (Spark, Bitcoin, etc.) -- Webhook notification confirms delivery - - - For JIT-funded quotes, do NOT call the execute endpoint. Grid automatically - processes the conversion when it receives your payment. - - -## Off-ramp flow (Crypto → Fiat) - -Enable users to convert cryptocurrency to fiat currency. - -### Pre-funding - -- Customer or platform holds crypto in internal accounts -- Balances can be funded via crypto deposits (Lightning, on-chain) - -### Quote and Execution - -- Create a quote specifying source (crypto account) and destination (bank account) -- Lock exchange rate and fees -- Execute the quote to initiate conversion -- Grid converts crypto to fiat and delivers to bank account - -### Fiat Delivery - -- Fiat delivered via local banking rails -- Settlement times vary by destination (instant to 1-3 business days) -- Webhook notification confirms delivery - -## Self-custody wallet integration - -Support for sending crypto to user-controlled wallets. - -- **Spark wallets**: Lightning-compatible wallets for instant, low-fee Bitcoin transfers -- **Validation**: Validate wallet addresses before creating external accounts -- **Limits**: Verify minimum and maximum amounts for crypto transfers -- **Monitoring**: Track transfer completion via webhooks - - - Spark wallets are recommended for crypto delivery as transfers complete within - seconds with minimal fees. - - -## Reconciling transactions - -Implement operational processes to keep your ledger in sync. - -- Process webhooks idempotently; map statuses (pending, processing, completed, failed) -- Tie transactions back to quotes and customers -- Track conversion rates and fees for accurate accounting -- Query for transactions by date range or customer as necessary - -## Testing in Sandbox - -Use Sandbox to build and validate end‑to‑end without moving real funds. - -- Simulate fiat funding using `/sandbox/send` endpoint -- Simulate crypto deposits using `/sandbox/internal-accounts/{accountId}/fund` endpoint or `/transfer-in` endpoint -- Test quote creation, conversion, and webhook lifecycles -- Validate customer onboarding flows with test data - -See the [Sandbox Testing](../platform-tools/sandbox-testing.mdx) page for more details. - -## Enabling Production - -When you're ready to go live: - -- Ensure adequate funding mechanisms are in place (for off-ramps) -- Confirm webhook security, monitoring, and alerting are configured -- Review rate limits, error handling, and idempotency -- Test conversion flows thoroughly in Sandbox -- Run final UAT in Sandbox, then request Production access from our team - - - Contact our team to enable Production and begin processing real conversions. - - -## Support - -If you need assistance with {rampsProductName}, please contact our support team at [support@lightspark.com](mailto:support@lightspark.com) or visit our support portal at [https://support.lightspark.com](https://support.lightspark.com). diff --git a/mintlify/ramps/onboarding/platform-configuration.mdx b/mintlify/ramps/onboarding/platform-configuration.mdx deleted file mode 100644 index e55a5765..00000000 --- a/mintlify/ramps/onboarding/platform-configuration.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -title: "Platform Configuration" ---- - -This guide explains how to configure your platform for ramp operations: get API credentials, configure webhooks, and set up supported currencies for conversions. - -## API credentials and authentication - -Create API credentials in the Grid dashboard. Credentials are scoped to an environment (Sandbox or Production) and cannot be used across environments. - -- **Authentication**: Use HTTP Basic Auth with your API key and secret in the `Authorization` header -- **Environment isolation**: Sandbox keys only work against Sandbox; Production keys only work against Production - - - Never share or expose your API secret. Rotate credentials periodically and - restrict access to authorized systems only. - - -### Example: HTTP Basic Auth - -```bash -# Using cURL's Basic Auth shorthand (-u): -curl -sS -X GET 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -## Base API path - -The base API path is consistent across environments; your credentials determine which environment you're accessing. - -**Base URL**: `https://api.lightspark.com/grid/2025-10-13` - - - The same base URL is used for both Sandbox and Production. Your API keys - determine which environment processes your requests. - - -## Supported currencies - -During onboarding, configure the currencies your platform will support for conversions. Grid supports: - -### Fiat Currencies - -- **USD** (United States Dollar) - Primary fiat currency -- **EUR** (Euro) -- **GBP** (British Pound) -- **MXN** (Mexican Peso) -- And more regional currencies - -### Cryptocurrencies - -- **Bitcoin** (BTC) via Spark, L1, or Lightning Network -- **Stablecoins** - -You can add or remove supported currencies anytime in the Grid dashboard. For pre-funded models, Grid automatically creates internal accounts for each supported currency. - - - Start with USD and BTC for the simplest on-ramp and off-ramp implementation, - then add additional currencies as needed. - - -## Webhooks and notifications - -Configure your webhook endpoint to receive real-time notifications about conversion status, transaction completion, and account balance updates. - -### Webhook setup - -1. **Create a public HTTPS endpoint** to receive webhook notifications -2. **Configure the endpoint URL** in the Grid dashboard -3. **Verify webhook signatures** using the Grid public key (provided in dashboard) -4. **Respond with 2xx status codes** to acknowledge receipt -5. **Process events idempotently** to handle duplicate deliveries - - - For development, use reverse proxies like ngrok to expose local endpoints. - Never use them in production. - - -### Webhook signature verification - -Webhooks use asymmetric (public/private key) signatures for security: - -- Verify the `X-Grid-Signature` header against the exact request body -- Use the Grid public key from your dashboard -- Reject webhooks with invalid signatures - - - The public key for verification is shown in the dashboard. Rotate keys when - instructed by Lightspark support. - - -### Test your webhook endpoint - -Use the webhook test endpoint to verify your endpoint configuration: - -```bash -curl -sS -X POST 'https://api.lightspark.com/grid/2025-10-13/webhooks/test' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -**Example test webhook payload:** - -```json -{ - "test": true, - "timestamp": "2025-10-03T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000001", - "type": "TEST" -} -``` - - - If your endpoint receives the test webhook and responds with a 2xx status - code, your webhook configuration is working correctly. - - -## Conversion limits - -Configure minimum and maximum amounts for conversions per currency: - -- **Minimum amounts**: Prevent uneconomical micro-conversions -- **Maximum amounts**: Manage risk and liquidity requirements -- **Per-transaction limits**: Configurable in the dashboard - -## Retrieve platform configuration - -You can retrieve your current platform configuration programmatically: - -```bash -curl -sS -X GET 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -**Response example:** - -```json -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "webhookEndpoint": "https://api.example.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"] - }, - { - "currencyCode": "BTC", - "minAmount": 1000, - "maxAmount": 10000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"] - } - ], - "createdAt": "2025-09-01T12:30:45Z", - "updatedAt": "2025-10-01T10:00:00Z" -} -``` - -## Update platform configuration - -Update your platform configuration using the PATCH endpoint: - -```bash -curl -sS -X PATCH 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "webhookEndpoint": "https://api.mycompany.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"] - }, - { - "currencyCode": "BTC", - "minAmount": 1000, - "maxAmount": 10000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"] - } - ] - }' -``` - - - Configuration changes take effect immediately. Test changes in Sandbox before - applying to Production. - diff --git a/mintlify/ramps/platform-tools/postman-collection.mdx b/mintlify/ramps/platform-tools/postman-collection.mdx deleted file mode 100644 index d11e88a6..00000000 --- a/mintlify/ramps/platform-tools/postman-collection.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Postman Collection" ---- - -import PostmanCollection from '/snippets/postman-collection.mdx'; - - - - diff --git a/mintlify/ramps/platform-tools/sandbox-testing.mdx b/mintlify/ramps/platform-tools/sandbox-testing.mdx deleted file mode 100644 index e0a12ecd..00000000 --- a/mintlify/ramps/platform-tools/sandbox-testing.mdx +++ /dev/null @@ -1,411 +0,0 @@ ---- -title: "Sandbox Testing" -description: "Test ramp flows safely without moving real funds" ---- - -The Grid Sandbox environment provides a complete testing environment for ramp operations, allowing you to validate on-ramp and off-ramp flows without using real money or cryptocurrency. - -## Sandbox overview - -Sandbox mirrors production behavior while using simulated funds: - -- **Same API endpoints**: Use identical API calls as production -- **Simulated funding**: Mock bank transfers and crypto deposits -- **Real webhooks**: Receive actual webhook notifications -- **No real money**: All transactions use test funds -- **Isolated environment**: Sandbox data never affects production - - - Sandbox is perfect for development, testing, and demonstrating ramp - functionality before going live. - - -## Getting started - -### Create sandbox credentials - -1. Log into the Grid dashboard -2. Navigate to **Settings** → **API Keys** -3. Click **Create API Key** and select **Sandbox** environment -4. Save your API key ID and secret securely - - - Sandbox credentials only work with the sandbox environment. They cannot access - production data or move real funds. - - -### Configure sandbox webhook - -Set up a webhook endpoint for sandbox notifications: - -```bash -curl -X PATCH 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "webhookEndpoint": "https://api.yourapp.dev/webhooks/grid" - }' -``` - - - Use tools like ngrok to expose local webhook endpoints during development: - `ngrok http 3000` - - -## Testing on-ramps (Fiat → Crypto) - -Simulate the complete on-ramp flow in sandbox: - -### Step 1: Create a test customer - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "platformCustomerId": "test_user_001", - "customerType": "INDIVIDUAL", - "fullName": "Alice Test", - "email": "alice@example.com", - "birthDate": "1990-01-15", - "address": { - "line1": "123 Test Street", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - }' -``` - -In sandbox, customers are automatically approved for testing. - -### Step 2: Create an on-ramp quote (just-in-time funding) - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "customerId": "Customer:sandbox001", - "currency": "USD" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:sandbox001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Test on-ramp conversion" - }' -``` - -The quote response includes payment instructions with a reference code. - -### Step 3: Simulate funding - -Use the sandbox endpoint to simulate receiving the fiat payment: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/sandbox/send' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "reference": "RAMP-ABC123", - "currencyCode": "USD", - "currencyAmount": 10000 - }' -``` - - - The reference code must match the one provided in the quote's payment - instructions. - - -### Step 4: Verify completion - -Within seconds, you'll receive a webhook notification confirming the on-ramp completed: - -```json -{ - "transaction": { - "id": "Transaction:sandbox025", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": { - "amount": 10000, - "currency": { "code": "USD" } - }, - "receivedAmount": { - "amount": 95000, - "currency": { "code": "BTC" } - }, - "settledAt": "2025-10-03T15:02:30Z" - }, - "type": "OUTGOING_PAYMENT" -} -``` - -## Testing off-ramps (Crypto → Fiat) - -Simulate the complete off-ramp flow: - -### Step 1: Fund internal account with crypto - -Simulate a Bitcoin deposit to the customer's internal account using the sandbox funding endpoint: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/sandbox/internal-accounts/InternalAccount:btc001/fund' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "amount": 10000000 - }' -``` - -Replace `InternalAccount:btc001` with your actual BTC internal account ID. - - - You'll receive an `ACCOUNT_STATUS` webhook showing the updated balance. - - -### Step 2: Create external bank account - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:sandbox001", - "currency": "USD", - "platformAccountId": "test_bank_001", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "123456001", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "bankName": "Test Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Alice Test", - "birthDate": "1990-01-15", - "nationality": "US", - "address": { - "line1": "123 Test Street", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } - }' -``` - - -In sandbox, you can use special account number patterns to test different scenarios. The **last 3 digits** determine the behavior: **002** (insufficient funds), **003** (account closed), **004** (transfer rejected), **005** (timeout/delayed failure). Any other ending succeeds normally. See "Testing transfer failures" below for details. - - -### Step 3: Create and execute off-ramp quote - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "accountId": "InternalAccount:sandbox_btc001" - }, - "destination": { - "accountId": "ExternalAccount:sandbox_bank001", - "currency": "USD" - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 5000000, - "description": "Test off-ramp conversion" - }' -``` - -Then execute the quote: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/execute' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" -``` - - - In sandbox, off-ramp conversions complete instantly. In production, bank - settlement may take 1-3 business days. - - -## Testing transfer failures - -### External account test patterns - -When creating external bank accounts in sandbox, use special account number patterns to simulate different transfer failure scenarios. The **last 3 digits** of the account number determine the test behavior: - -| Last Digits | Behavior | Use Case | -|-------------|----------|----------| -| **002** | Insufficient funds | Simulates bank account with insufficient balance | -| **003** | Account closed/invalid | Simulates closed or non-existent account | -| **004** | Transfer rejected | Simulates bank rejecting the transfer (compliance, limits, etc.) | -| **005** | Timeout/delayed failure | Transaction stays pending ~30s, then fails | -| **Any other** | Success | All transfers complete normally | - -**Example - Testing Insufficient Funds:** - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:sandbox001", - "currency": "USD", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "000000002", // Will trigger insufficient funds - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Test User" - } - } - }' -``` - -When you create an off-ramp quote to this account and execute it, the transaction will fail immediately with an insufficient funds error. - - -These patterns work for all account types: US account numbers, IBANs, CLABEs, etc. Just ensure the identifier ends with the appropriate -test digits. For scenarios like PIX and UPI, where there's a domain part involved, append the test digits to the user name part. -For example, if testing email addresses as a PIX key, the full identifier would be "testuser.002@pix.com.br" to trigger the -insufficient funds scenario. - - -## Test scenarios - -### Successful conversions - -The complete on-ramp and off-ramp flows described in the sections above demonstrate successful conversion scenarios. For quick reference: - -**On-ramp test (USD → BTC):** -1. Create customer and quote with payment instructions -2. Use `/sandbox/send` to simulate funding -3. Verify completion via webhook - -**Off-ramp test (BTC → USD):** -1. Fund BTC internal account with `/sandbox/internal-accounts/{accountId}/fund` -2. Create external bank account (use default account number for success) -3. Create and execute quote -4. Verify completion via webhook - -### Failed conversions - -Test error scenarios systematically using the magic account patterns: - -**1. Test external account insufficient funds (002):** - -```bash -# Create account with insufficient funds pattern -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:sandbox001", - "currency": "USD", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "000000002", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Test User" - } - } - }' - -# Attempt off-ramp to this account - will fail immediately -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/execute' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" -# Response: 400 Bad Request with insufficient funds error -``` - -**2. Test account closed (003):** - -```bash -# Create account with closed pattern -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -d '{"accountNumber": "000000003", ...}' - -# Attempt to use - will fail with account closed error -``` - -**3. Test insufficient balance in internal account:** - -```bash -# Create quote from empty internal account -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "accountId": "InternalAccount:empty_btc" - }, - "destination": { - "accountId": "ExternalAccount:bank001", - "currency": "USD" - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000000 - }' - -# Execute will fail with insufficient balance error -``` - -**4. Test invalid wallet address:** - -```bash -# Attempt quote with invalid Spark address -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -u "$SANDBOX_API_KEY:$SANDBOX_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "destination": { - "externalAccountDetails": { - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "invalid_address" - } - } - } - }' -# Response: 400 Bad Request with validation error -``` - -## Moving to Production - -When you're ready to move to production: - -1. Generate production API tokens in the dashboard -2. Swap those credentials for the sandbox credentials in your environment variables -3. Remove any sandbox-specific test patterns from your code -4. Configure production webhook endpoints -5. Test with small amounts first - -## Next steps - -- [Webhooks](/ramps/platform-tools/webhooks) - Handle real-time notifications -- [Fiat-to-Crypto Conversion](/ramps/conversion-flows/fiat-crypto-conversion) - Implement production flows -- [Self-Custody Wallets](/ramps/conversion-flows/self-custody-wallets) - Advanced wallet integration -- [Platform Configuration](/ramps/onboarding/platform-configuration) - Configure production settings -- [API Reference](/api-reference) - Complete API documentation diff --git a/mintlify/ramps/platform-tools/webhooks.mdx b/mintlify/ramps/platform-tools/webhooks.mdx deleted file mode 100644 index 85e319a8..00000000 --- a/mintlify/ramps/platform-tools/webhooks.mdx +++ /dev/null @@ -1,394 +0,0 @@ ---- -title: "Webhooks" -description: "Receive real-time notifications for ramp conversions, account updates, and transaction status" ---- - -Webhooks provide real-time notifications about ramp operations, allowing you to respond immediately to conversion completions, account balance changes, and transaction status updates. - -## Webhook events for ramps - -Grid sends webhooks for key events in the ramp lifecycle: - -### Conversion events - - - - Sent when a conversion (on-ramp or off-ramp) completes, fails, or changes status. - - ```json - { - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000025", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": { - "amount": 10000, - "currency": { "code": "USD", "decimals": 2 } - }, - "receivedAmount": { - "amount": 95000, - "currency": { "code": "BTC", "decimals": 8 } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "settledAt": "2025-10-03T15:02:30Z", - "exchangeRate": 9.5, - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006" - }, - "timestamp": "2025-10-03T15:03:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000030", - "type": "OUTGOING_PAYMENT" - } - ``` - - - Use this webhook to update your UI, credit customer accounts, and trigger post-conversion workflows. - - - - - Sent when internal account balances change (deposits, conversions, withdrawals). - - ```json - { - "accountId": "InternalAccount:btc456", - "oldBalance": { - "amount": 10000000, - "currency": { "code": "BTC", "decimals": 8 } - }, - "newBalance": { - "amount": 5000000, - "currency": { "code": "BTC", "decimals": 8 } - }, - "timestamp": "2025-10-03T15:03:00Z", - "webhookId": "Webhook:webhook001", - "type": "ACCOUNT_STATUS" - } - ``` - - - Critical for tracking crypto deposits (for off-ramps) and fiat balance changes. - - - - - Sent when customer KYC verification completes (required before conversions). - - ```json - { - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "KYC_STATUS", - "timestamp": "2025-10-03T14:32:00Z", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "kycStatus": "APPROVED", - "platformCustomerId": "user_12345" - } - ``` - - - Enable ramp access immediately when KYC status changes to `APPROVED`. - - - - -## Webhook configuration - -Configure your webhook endpoint in the Grid dashboard or via API: - -```bash -curl -X PATCH 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "webhookEndpoint": "https://api.yourapp.com/webhooks/grid" - }' -``` - - - Your webhook endpoint must be publicly accessible over HTTPS and respond - within 30 seconds. - - -## Webhook verification - -Always verify webhook signatures to ensure authenticity: - -### Verification steps - - - - Get the `X-Grid-Signature` header from the webhook request. - - ```javascript - const signature = req.headers['x-grid-signature']; - ``` - - - - Retrieve the Grid public key from your dashboard (provided during onboarding). - ```javascript const publicKey = process.env.GRID_PUBLIC_KEY; ``` - - - - Use the public key to verify the signature against the raw request body. - - ```javascript - const crypto = require('crypto'); - - function verifyWebhookSignature(payload, signature, publicKey) { - const verify = crypto.createVerify('SHA256'); - verify.update(payload); - verify.end(); - - return verify.verify(publicKey, signature, 'base64'); - } - - // In your webhook handler - const rawBody = JSON.stringify(req.body); - const isValid = verifyWebhookSignature(rawBody, signature, publicKey); - - if (!isValid) { - return res.status(401).json({ error: 'Invalid signature' }); - } - ``` - - - - - The signature is created using secp256r1 (P-256) asymmetric cryptography with - SHA-256 hashing. - - -## Handling ramp webhooks - -### On-ramp completion - -Handle successful fiat-to-crypto conversions: - -```javascript -app.post("/webhooks/grid", async (req, res) => { - // Verify signature - if (!verifySignature(req.body, req.headers["x-grid-signature"])) { - return res.status(401).end(); - } - - const { type, transaction } = req.body; - - if (type === "OUTGOING_PAYMENT" && transaction.status === "COMPLETED") { - // On-ramp completed (USD → BTC) - if ( - transaction.sentAmount.currency.code === "USD" && - transaction.receivedAmount.currency.code === "BTC" - ) { - // Update user's transaction history - await db.transactions.create({ - userId: transaction.customerId, - type: "ON_RAMP", - amountUsd: transaction.sentAmount.amount, - amountBtc: transaction.receivedAmount.amount, - rate: transaction.exchangeRate, - status: "COMPLETED", - completedAt: new Date(transaction.settledAt), - }); - - // Notify user - await sendNotification(transaction.customerId, { - title: "Bitcoin purchased!", - message: `You received ${formatBtc( - transaction.receivedAmount.amount - )} BTC`, - }); - } - } - - res.status(200).json({ received: true }); -}); -``` - -### Off-ramp completion - -Handle successful crypto-to-fiat conversions: - -```javascript -if (type === "OUTGOING_PAYMENT" && transaction.status === "COMPLETED") { - // Off-ramp completed (BTC → USD) - if ( - transaction.sentAmount.currency.code === "BTC" && - transaction.receivedAmount.currency.code === "USD" - ) { - // Update user's balance and transaction history - await db.transactions.create({ - userId: transaction.customerId, - type: "OFF_RAMP", - amountBtc: transaction.sentAmount.amount, - amountUsd: transaction.receivedAmount.amount, - rate: transaction.exchangeRate, - status: "COMPLETED", - bankAccountId: transaction.destination.accountId, - completedAt: new Date(transaction.settledAt), - }); - - // Notify user - await sendNotification(transaction.customerId, { - title: "Cash out completed!", - message: `$${formatUsd( - transaction.receivedAmount.amount - )} sent to your bank account`, - }); - } -} -``` - -### Balance updates - -Track crypto deposits for off-ramp liquidity: - -```javascript -if (type === "ACCOUNT_STATUS") { - const { accountId, newBalance, oldBalance } = req.body; - - // Crypto deposit detected - if ( - newBalance.currency.code === "BTC" && - newBalance.amount > oldBalance.amount - ) { - const depositAmount = newBalance.amount - oldBalance.amount; - - // Record deposit - await db.deposits.create({ - accountId, - currency: "BTC", - amount: depositAmount, - newBalance: newBalance.amount, - }); - - // Check if user has pending off-ramp - const pendingOffRamp = await db.offRamps.findPending(accountId); - if (pendingOffRamp && newBalance.amount >= pendingOffRamp.requiredAmount) { - // Auto-execute pending off-ramp - await executeOffRamp(pendingOffRamp.id); - } - } -} -``` - -## Best practices - - - - Handle duplicate webhooks gracefully using webhook IDs: - - ```javascript - const { webhookId } = req.body; - - // Check if already processed - const existing = await db.webhooks.findUnique({ where: { webhookId } }); - if (existing) { - return res.status(200).json({ received: true }); - } - - // Process webhook - await processRampWebhook(req.body); - - // Record webhook ID - await db.webhooks.create({ - data: { webhookId, processedAt: new Date() } - }); - ``` - - - - Transaction IDs are unique identifiers for conversions: - - ```javascript - const { transaction } = req.body; - - // Upsert transaction (handles duplicates) - await db.transactions.upsert({ - where: { gridTransactionId: transaction.id }, - update: { status: transaction.status }, - create: { - gridTransactionId: transaction.id, - customerId: transaction.customerId, - status: transaction.status, - // ... other fields - }, - }); - ``` - - - - Grid retries failed webhooks with exponential backoff. Ensure your endpoint can handle retries: - - ```javascript - app.post('/webhooks/grid', async (req, res) => { - try { - await processWebhook(req.body); - res.status(200).json({ received: true }); - } catch (error) { - console.error('Webhook processing error:', error); - - // Return 5xx for retryable errors - if (error.retryable) { - res.status(503).json({ error: 'Temporary failure' }); - } else { - // Return 200 for non-retryable to prevent retries - res.status(200).json({ error: error.message }); - } - } - }); - ``` - - - - Track webhook delivery and processing: - - ```javascript - // Log webhook metrics - await metrics.increment('webhooks.received', { - type: req.body.type, - status: req.body.transaction?.status, - }); - - // Track processing time - const start = Date.now(); - await processWebhook(req.body); - const duration = Date.now() - start; - - await metrics.histogram('webhooks.processing_time', duration, { - type: req.body.type, - }); - ``` - - - -## Testing webhooks - -Test webhook handling using the test endpoint: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/webhooks/test' \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -This sends a test webhook to your configured endpoint: - -```json -{ - "test": true, - "timestamp": "2025-10-03T14:32:00Z", - "webhookId": "Webhook:test001", - "type": "TEST" -} -``` - - - Verify your endpoint receives the test webhook and responds with a 200 status - code. - - -## Next steps - -- [Sandbox Testing](/ramps/platform-tools/sandbox-testing) - Test ramp flows end-to-end -- [Platform Configuration](/ramps/onboarding/platform-configuration) - Configure webhook endpoint -- [Fiat-to-Crypto Conversion](/ramps/conversion-flows/fiat-crypto-conversion) - Implement conversion flows -- [API Reference](/api-reference) - Complete webhook documentation diff --git a/mintlify/ramps/quickstart.mdx b/mintlify/ramps/quickstart.mdx deleted file mode 100644 index 68b1a946..00000000 --- a/mintlify/ramps/quickstart.mdx +++ /dev/null @@ -1,280 +0,0 @@ ---- -title: "Quickstart" -description: "Complete guide for converting fiat to crypto (on-ramp) and delivering Bitcoin to a Spark wallet" ---- - -import KycRegulated from '/snippets/kyc/kyc-regulated.mdx'; -import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx'; - -This guide walks you through the complete process of converting USD to Bitcoin and sending it to a Spark wallet using just-in-time (JIT) funding. - -## Prerequisites - -Before starting this guide, ensure you have: - -- A Grid API account with valid authentication credentials -- Access to the Grid API endpoints (production or sandbox) -- A webhook endpoint configured to receive notifications -- A Spark wallet address where the Bitcoin will be delivered - -## Overview - -The on-ramp process consists of the following steps: - -1. **Create a customer** via the API -2. **Create a quote** for the USD-to-BTC conversion with current exchange rate -3. **Fund the quote** using the provided payment instructions (JIT funding) -4. **Receive webhook notification** confirming Bitcoin delivery to the Spark wallet - ---- - -## Step 1: Customer Onboarding - -If your platform is a regulated financial institution that already has a KYC/KYB process in place, -you can create a customer directly via the API. However, if your platform is not regulated, you must use the hosted KYC/KYB -link flow to onboard your customers. - - - - - - - - - - -## Step 2: Create a Quote for Fiat-to-Crypto Conversion - -Create a quote to convert USD to Bitcoin and deliver it to a Spark wallet. The quote will provide the current exchange rate and payment instructions for funding. - -### Request - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "On-ramp: Buy $100 of Bitcoin" - }' -``` - - - **Combined External Account Creation**: The `externalAccountDetails` option - automatically creates the external account (Spark wallet) and uses it as the - destination for the Bitcoin transfer in a single API call. If you want to reuse - the same external accounts for many quotes, you can add them using the - `/external-accounts` endpoint, and then use the `accountId` in the quote creation request. - - -### Response - -```json -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "status": "PENDING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456", - "currency": "BTC" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "BTC", - "name": "Bitcoin", - "symbol": "₿", - "decimals": 8 - }, - "totalSendingAmount": 10000, - "totalReceivingAmount": 83333, - "exchangeRate": 8.3333, - "feesIncluded": 250, - "paymentInstructions": [ - { - "instructionsNotes": "Include reference code in transfer memo", - "accountOrWalletInfo": { - "reference": "RAMP-ABC123", - "accountType": "US_ACCOUNT", - "accountNumber": "9876543210", - "routingNumber": "021000021", - "accountHolderName": "Lightspark Payments FBO Customer", - "bankName": "JP Morgan Chase" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ] -} -``` - -The quote shows: - -- **Sending**: \$100.00 USD (including $2.50 fee) -- **Receiving**: 0.00083333 BTC (83,333 satoshis) -- **Exchange rate**: 8.3333 sats per USD cent (~$120,000 per BTC) -- **Quote expires**: In 5 minutes -- **Payment instructions**: Bank account details and reference code for funding - - - For JIT-funded quotes, do NOT call the `/quotes/{quoteId}/execute` endpoint. - Simply fund using the payment instructions, and Grid will automatically - execute the conversion upon receiving your payment. The execute endpoint is - used for quotes with an internal account or pullable external account as the source. - - ---- - -## Step 3: Fund the Quote (Just-in-Time) - -In production, you would initiate a real-time push payment (ACH, RTP, wire, etc.) to the bank account provided in `paymentInstructions`, making sure to include the exact reference code `RAMP-ABC123` in the transfer memo. - - - -In Sandbox, you can simulate funding using the `/sandbox/send` endpoint: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/sandbox/send" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "reference": "RAMP-ABC123", - "currencyCode": "USD", - "currencyAmount": 10000 - }' -``` - -**Response:** - -```json -{ - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000025", - "status": "PROCESSING", - "type": "OUTGOING", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006" -} -``` - - - - -In production, your customer or platform would initiate a bank transfer to the provided account details, include the reference code `RAMP-ABC123` in the transfer memo, and wait for Grid to detect the incoming payment (typically 1-3 business days for ACH, instant for RTP/wire). You'll receive a webhook notification when Bitcoin is delivered to the Spark wallet. - - - - - The reference code is critical for matching your payment to the quote. Always - include it exactly as provided in the payment instructions. - - ---- - -## Step 4: Receive Completion Webhook - -Once Grid receives your payment and completes the USD-to-BTC conversion and delivery, you'll receive a webhook notification: - -```json -{ - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000025", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": { - "amount": 10000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 83333, - "currency": { - "code": "BTC", - "name": "Bitcoin", - "symbol": "₿", - "decimals": 8 - } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "settledAt": "2025-10-03T15:02:30Z", - "createdAt": "2025-10-03T15:00:00Z", - "description": "On-ramp: Buy $100 of Bitcoin", - "exchangeRate": 8.3333, - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000006", - "paymentInstructions": [ - { - "instructionsNotes": "Include reference code in transfer memo", - "accountOrWalletInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "1234567890", - "routingNumber": "021000021", - "bankName": "Chase Bank", - "referenceCode": "REF123456" - } - } - ] - }, - "timestamp": "2025-10-03T15:03:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000030", - "type": "OUTGOING_PAYMENT" -} -``` - - - The customer now has 83,333 satoshis (0.00083333 BTC) in their Spark wallet! - - ---- - -## Summary - -You've successfully completed a fiat-to-crypto on-ramp! Here's what happened: - -1. ✅ Created a customer via API -2. ✅ Created a quote with exchange rate and payment instructions -3. ✅ Funded the quote using JIT payment (simulated in sandbox) -4. ✅ Bitcoin automatically converted and delivered to Spark wallet - -## Next Steps - -- **Off-ramps**: Learn how to convert crypto to fiat in the [Crypto-to-Fiat guide](/ramps/conversion-flows/fiat-crypto-conversion) -- **Self-custody wallets**: Explore advanced wallet integration in the [Self-Custody Wallets guide](/ramps/conversion-flows/self-custody-wallets) -- **Webhook verification**: Implement signature verification for security (see [Webhooks guide](/ramps/platform-tools/webhooks)) -- **Sandbox testing**: Learn more about testing in the [Sandbox Testing guide](/ramps/platform-tools/sandbox-testing) - -## Related Resources - -- [API Reference](/api-reference) - Complete API documentation -- [Implementation Overview](/ramps/onboarding/implementation-overview) - High-level architecture and flow -- [Platform Configuration](/ramps/onboarding/platform-configuration) - Configure your platform settings diff --git a/mintlify/ramps/terminology.mdx b/mintlify/ramps/terminology.mdx deleted file mode 100644 index 8e1a67ab..00000000 --- a/mintlify/ramps/terminology.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Core Concepts" -description: "Core concepts and terminology for the Grid API" ---- - -import Terminology from '/snippets/terminology.mdx'; - - - - diff --git a/mintlify/ramps/webhooks.mdx b/mintlify/ramps/webhooks.mdx deleted file mode 100644 index e636c6ff..00000000 --- a/mintlify/ramps/webhooks.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Webhooks" ---- - -import Webhooks from '/snippets/webhooks.mdx'; - - \ No newline at end of file diff --git a/mintlify/rewards/developer-guides/configuring-customers.mdx b/mintlify/rewards/developer-guides/configuring-customers.mdx deleted file mode 100644 index bc15a9af..00000000 --- a/mintlify/rewards/developer-guides/configuring-customers.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: "Configuring Customers" -description: "Complete guide to creating and managing customers for Bitcoin rewards" ---- - -import KycRegulated from '/snippets/kyc/kyc-regulated.mdx' -import KycUnregulated from '/snippets/kyc/kyc-unregulated.mdx' -import KycWebhooks from '/snippets/kyc/kyc-webhooks.mdx' - -This guide covers everything you need to know about creating and managing customers in the Grid API for Bitcoin rewards distribution. - -## Overview - -Customers are the *payers* of your Bitcoin rewards. They can be individuals or businesses. Each customer in the Grid system has: - -- **System-generated ID**: Unique identifier assigned by Grid (e.g., `Customer:019542f5-b3e7-1d02-0000-000000000001`) -- **Customer Type**: Either `INDIVIDUAL` or `BUSINESS` -- **KYC Status**: Indicates if the customer has been verified and approved. -- **Internal Accounts**: Automatically created for each supported currency upon customer creation -- **Platform Customer ID**: Optional field to link to your own user/customer ID - - -The `platformCustomerId` field is optional but recommended. Use your existing user IDs to maintain a simple mapping between your system and Grid. - - -## Customer Onboarding - - - - - - - - - - - -When a customer is created successfully, internal accounts are automatically created for each currency configured on your platform. These accounts can be used as sources or destinations for transfers. - - -### Hanlding KYC/KYC Webhooks - - - -## Listing Customers - -Retrieve a paginated list of customers with optional filtering: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers?limit=20&customerType=INDIVIDUAL" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -### Query Parameters - - -Filter by your platform's customer identifier - - - -Filter by customer type (`INDIVIDUAL` or `BUSINESS`) - - - -Filter customers created after this timestamp (ISO 8601) - - - -Filter customers created before this timestamp (ISO 8601) - - - -Filter customers updated after this timestamp (ISO 8601) - - - -Filter customers updated before this timestamp (ISO 8601) - - - -Whether to include deleted customers in results (default: false) - - - -Maximum number of results per page (default: 20, max: 100) - - - -Pagination cursor from previous response - - -### Response - -```json -{ - "data": [ - { - "id": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "user_12345", - "customerType": "INDIVIDUAL", - "kycStatus": "APPROVED", - "fullName": "Jane Doe", - "birthDate": "1992-03-25", - "address": { - "line1": "123 Pine Street", - "line2": "Unit 501", - "city": "Seattle", - "state": "WA", - "postalCode": "98101", - "country": "US" - }, - "createdAt": "2025-10-03T12:00:00Z", - "updatedAt": "2025-10-03T12:00:00Z", - "isDeleted": false - } - ], - "hasMore": true, - "nextCursor": "MjAyNS0xMC0wM1QxMjowMDowMFo=", - "totalCount": 152 -} -``` - - -To find a specific customer by your platform ID, use: `GET /customers?platformCustomerId=user_12345` - - -## Retrieving a Single Customer - -Get detailed information about a specific customer: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -## Deleting Customers - -Delete a customer by their system-generated ID: - -```bash -curl -X DELETE "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - -Deleting a customer is permanent and will prevent them from receiving future payments. Their transaction history will be preserved but the customer record will be marked as deleted. - - -## Related Resources - -- [API Reference: Customers](/api-reference/customers/add-a-new-customer) - Complete customer API documentation -- [Quick Start Guide](/rewards/quickstart) - End-to-end Bitcoin rewards walkthrough -- [Handling Webhooks](/rewards/platform-tools/webhooks) - Implement webhook handling for real-time notifications diff --git a/mintlify/rewards/developer-guides/distributing-rewards.mdx b/mintlify/rewards/developer-guides/distributing-rewards.mdx deleted file mode 100644 index dbce54a0..00000000 --- a/mintlify/rewards/developer-guides/distributing-rewards.mdx +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: "Paying out Bitcoin rewards" -description: "Send Bitcoin rewards to customers using the Grid API" ---- - -This guide covers how to distribute Bitcoin rewards to your customers, including quote creation, execution options, and tracking delivery. - -## Overview - -Distributing Bitcoin rewards involves creating a quote that converts your fiat balance (typically USD) to Bitcoin and sends it to the customer's wallet. You have flexibility in how you lock amounts, register destinations, and execute transfers. - -## Basic Flow - -1. **Create a quote** - Specify source account, destination, and amount -2. **Execute the quote** - Either immediately or after review -3. **Monitor completion** - Track via webhooks or polling - -## Finding your platform's internal account - -In this guide, we'll use the platform's USD internal account as the funding source for the rewards. -You can find your platform's internal account by listing all internal accounts for your platform. - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/platform/internal-accounts" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Response: - -```json -{ - "data": [ - { - "id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "balance": { - "amount": 10000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "fundingPaymentInstructions": [...], - "createdAt": "2025-10-03T12:00:00Z", - "updatedAt": "2025-10-03T12:00:00Z" - } - ] -} -``` - -## Creating a Quote - -The core request specifies your platform's internal account as the source and the customer's wallet as the destination. - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true, - "description": "Weekly reward payout" - }' -``` - -Response: - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020", - "status": "PROCESSING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456", - "currency": "BTC" - }, - "sendingCurrency": { - "code": "USD", - "decimals": 2 - }, - "receivingCurrency": { - "code": "BTC", - "decimals": 8 - }, - "totalSendingAmount": 100, - "totalReceivingAmount": 810, - "exchangeRate": 8.1, - "feesIncluded": 5, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025" -} -``` - -## Locking Amount: Sending vs. Receiving - -When creating a quote, you can choose to either lock the amount you're sending (fiat) or the amount the customer receives (Bitcoin). - -### Lock Sending Amount - -Use this when you want to send a fixed dollar amount (e.g., $1.00 reward). - -```json -{ - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100 // $1.00 in cents -} -``` - -The customer receives whatever Bitcoin this amount buys at the current rate. - -### Lock Receiving Amount - -Use this when you want the receiver to receive a specific Bitcoin amount (e.g., 1000 sats). - -```json -{ - "lockedCurrencySide": "RECEIVING", - "lockedCurrencyAmount": 1000 // 1000 satoshis -} -``` - -Your platform account is debited whatever fiat amount is needed to send that Bitcoin amount. - - - For consistent dollar-value rewards, use `SENDING`. For consistent - Bitcoin-value rewards, use `RECEIVING`. - - -## Execution Options - -### Immediate Execution (Market Order) - -Set `immediatelyExecute: true` to create and execute the quote in one step. This is ideal for automated reward distribution where you accept the current market rate. - -```json -{ - "immediatelyExecute": true -} -``` - -The quote is created and executed immediately. You receive a `transactionId` in the response. - -### Two-Step Execution (Review Before Sending) - -Omit `immediatelyExecute` or set it to `false` to review the quote before executing. - -```json -{ - "immediatelyExecute": false // or omit this field -} -``` - -The response will be the same as the immediate execution response, but the status will be `PENDING` and you'll have until the quote's `expiresAt` timestamp to execute the quote. - -After reviewing the quote's exchange rate and fees, execute it: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/execute" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - - Quotes expire after a short time (typically 5 minutes). You must execute - before expiration. - - -## Destination Options - -### Inline External Account Creation - -Use `externalAccountDetails` to create the destination wallet on the fly. This is perfect for one-time or infrequent payouts. - -```json -{ - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - } -} -``` - -The external account is created automatically and returned in the quote response. - -### Pre-Registered External Account - -If you've already registered the external account, reference it by ID. This is more efficient for recurring rewards to the same wallets. - -First, create the external account: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers/external-accounts" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - }' -``` - -Then reference it in `/quotes`: - -```json -{ - "destination": { - "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456" - } -} -``` - - - Pre-register external accounts for customers who receive regular rewards. This - avoids duplicate account creation and improves performance. - - -## Tracking Delivery - -### Webhook Notification - -When the Bitcoin transfer completes, you'll receive a webhook: - -```json -{ - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000025", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": { - "amount": 100, - "currency": { "code": "USD" } - }, - "receivedAmount": { - "amount": 810, - "currency": { "code": "BTC" } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "settledAt": "2025-10-03T15:01:45Z" - }, - "type": "OUTGOING_PAYMENT" -} -``` - -### Polling Status - -Alternatively, poll the quote or transaction endpoint: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Or: - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/transactions/{transactionId}" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -## Common Patterns - -### Fixed Dollar Rewards (e.g., $1.00 per action) - -```json -{ - "source": { "accountId": "InternalAccount:..." }, - "destination": { "externalAccountDetails": { ... } }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true -} -``` - -### Fixed Satoshi Rewards (e.g., 1000 sats per action) - -```json -{ - "source": { "accountId": "InternalAccount:..." }, - "destination": { "accountId": "ExternalAccount:..." }, - "lockedCurrencySide": "RECEIVING", - "lockedCurrencyAmount": 1000, - "immediatelyExecute": true -} -``` - -### Review Before Sending - -```json -{ - "source": { "accountId": "InternalAccount:..." }, - "destination": { "accountId": "ExternalAccount:..." }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 5000, - "immediatelyExecute": false -} -``` - -Then review the exchange rate and fees, and execute: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/{quoteId}/execute" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -## Best Practices - - - Use `immediatelyExecute: true` for automated, small-dollar rewards where you - accept market rates. - - - - Pre-register external accounts for customers receiving recurring rewards to - improve performance. - - - - Set up webhook handlers to track completion status and update your reward - records. - - - - Ensure your platform's internal account has sufficient balance before - distributing rewards. Monitor balances via the `/platform/internal-accounts` - endpoint or account status webhooks. - - -## Related Resources - -- [Quick Start Guide](/rewards/quickstart) - End-to-end Bitcoin rewards walkthrough -- [Configuring Customers](/rewards/developer-guides/configuring-customers) - Customer creation and management -- [API Reference: Quotes](/api-reference/quotes/create-a-transfer-quote) - Complete quote API documentation -- [Handling Webhooks](/rewards/platform-tools/webhooks) - Webhook security and implementation diff --git a/mintlify/rewards/developer-guides/external-accounts.mdx b/mintlify/rewards/developer-guides/external-accounts.mdx deleted file mode 100644 index 5da1cd74..00000000 --- a/mintlify/rewards/developer-guides/external-accounts.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "External Accounts" -description: "Add and manage external funding sources and wallets as payment destinations for rewards" ---- - -import ExternalAccounts from '/snippets/external-accounts.mdx'; - - - -## Next steps - - - - Simplify external account setup with Plaid Link for instant bank verification - - - - Learn how to pay out Bitcoin rewards using external accounts - - - - View complete API documentation for external accounts - - diff --git a/mintlify/rewards/developer-guides/implementation-overview.mdx b/mintlify/rewards/developer-guides/implementation-overview.mdx deleted file mode 100644 index 51fe767f..00000000 --- a/mintlify/rewards/developer-guides/implementation-overview.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: "Implementation Overview" ---- - -This page gives you a 10,000‑ft view of an end‑to‑end Bitcoin rewards implementation. It is intentionally generalized to cover the main building blocks. The detailed guides that follow provide concrete fields, edge cases, and step‑by‑step instructions. - - -This overview highlights the main building blocks: platform setup, funding, customer onboarding, external account creation, and Bitcoin reward distribution. - - -## Platform configuration - -Configure your platform once before building user flows. - -- Provide webhook endpoints for transaction and account status notifications -- Generate API credentials for Sandbox (and later Production) -- Configure supported currencies (typically a Fiat currency and BTC or stables for rewards) - -## Platform account funding - -Fund your platform's internal account to enable instant Bitcoin rewards distribution. - -- **Platform internal accounts**: Automatically created for each supported currency when your platform is set up -- **Funding options**: ACH transfer, wire transfer, or crypto deposits (BTC, Stablecoins) -- **Balance management**: Monitor balances via API or webhook notifications to ensure sufficient funds for rewards distribution - - -For Bitcoin rewards, maintaining a prefunded USD balance allows you to instantly purchase and distribute Bitcoin to customers without delays. - - -## Onboarding customers - -For rewards, the only entity who needs to be KYB'd is the entity paying for the reward. This can be you, the platform, or -your business customers that want to pay out rewards to their end users. -All you need in order to pay out a reward is the wallet address. -No need to collect extra personal information or go through the full hosted KYC flow for end users! -To generate a spark wallet, you can use a tool like [Privy](https://privy.io) or the Spark SDK directly. - -## External account creation - -Register external accounts where customers will receive their Bitcoin rewards. - -- **Spark wallets**: Lightning-compatible Spark wallets for instant, low-fee transfers of Bitcoin and Stablecoins -- **Other cryptocurrency wallets**: Support for various Bitcoin destination types -- Capture wallet addresses and validate formats where applicable - - -Spark wallets are recommended for Bitcoin rewards as transfers complete within seconds with minimal fees. - - -## Distributing rewards - -Send Bitcoin rewards to customers using a streamlined quote-and-execute flow. - -- **Create and execute quote**: Specify source (your platform's USD account), destination (customer's Spark wallet), and amount -- **Currency conversion**: Platform handles USD to BTC conversion at current market rates -- **Immediate execution**: Use `immediatelyExecute: true` for one-step market-order style distribution -- **Monitor status**: Track completion via webhooks or polling - - -For recurring small-dollar rewards, use `immediatelyExecute: true` to skip the quote confirmation step and send at the current market rate. - - -## Reconciling transactions - -Implement operational processes to keep your ledger in sync. - -- Process webhooks idempotently; map statuses (pending, processing, completed, failed) -- Tie transactions back to customers and reward events -- Monitor platform account balances to ensure adequate funding -- Query for transactions by date range or customer as necessary - -## Testing in Sandbox - -Use Sandbox to build and validate end‑to‑end without moving real funds. - -- Simulate account funding using `/sandbox/internal-accounts/{accountId}/fund` endpoint -- Test quote creation, execution, and webhook lifecycles -- Validate customer onboarding flows with test data - -## Enabling Production - -When you're ready to go live: - -- Ensure adequate funding in your production platform account -- Confirm webhook security, monitoring, and alerting are in place -- Review rate limits, error handling, and idempotency -- Run final UAT in Sandbox, then request Production access from our team - - -Contact our team to enable Production and begin distributing Bitcoin rewards. - diff --git a/mintlify/rewards/developer-guides/internal-accounts.mdx b/mintlify/rewards/developer-guides/internal-accounts.mdx deleted file mode 100644 index b44a554d..00000000 --- a/mintlify/rewards/developer-guides/internal-accounts.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "Internal Accounts" -description: "Learn how to manage and fund internal accounts for holding platform and customer funds" ---- - -import InternalAccounts from '/snippets/internal-accounts.mdx'; - - - -## Next steps - - - -Learn how to add customer wallets as reward destinations - - - -Simplify bank account verification with Plaid Link - - - -Use internal account balances to send Bitcoin rewards - - - -View complete API documentation for internal accounts - - diff --git a/mintlify/rewards/developer-guides/listing-transactions.mdx b/mintlify/rewards/developer-guides/listing-transactions.mdx deleted file mode 100644 index 822137a8..00000000 --- a/mintlify/rewards/developer-guides/listing-transactions.mdx +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: "Listing Transactions" -description: "Query and track Bitcoin reward payment history with filtering and pagination" ---- - -Retrieve transaction history for Bitcoin rewards distributed through your platform. Transactions are returned in descending order (most recent first) and are paginated. - -## Basic request - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -```json response -{ - "data": [ - { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000025", - "status": "COMPLETED", - "type": "OUTGOING", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456", - "currency": "BTC" - }, - "sentAmount": { - "amount": 100, - "currency": { - "code": "USD", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 810, - "currency": { - "code": "BTC", - "symbol": "₿", - "decimals": 8 - } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "user_789", - "description": "Weekly reward payout", - "exchangeRate": 8.1, - "settledAt": "2025-10-03T15:01:45Z", - "createdAt": "2025-10-03T15:00:00Z" - } - ], - "hasMore": true, - "nextCursor": "eyJpZCI6IlRyYW5zYWN0aW9uOjAxOTU0MmY1LWIzZTctMWQwMi0wMDAwLTAwMDAwMDAwMDAyNSJ9", - "totalCount": 142 -} -``` - -## Common filtering patterns - -### Rewards for a specific customer - -Get all Bitcoin rewards sent to a customer: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Or use your platform's customer ID: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?platformCustomerId=user_789' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -### Rewards by date range - -Get all rewards distributed in a specific period: - -```bash -# October 2025 rewards -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?startDate=2025-10-01T00:00:00Z&endDate=2025-10-31T23:59:59Z' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Dates must be in ISO 8601 format (e.g., `2025-10-03T15:00:00Z`). - -### Failed rewards - -Track rewards that failed to complete: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?status=FAILED&type=OUTGOING' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -**Transaction statuses:** - -- `PENDING` - Reward initiated, awaiting processing -- `PROCESSING` - Bitcoin purchase and transfer in progress -- `COMPLETED` - Reward successfully delivered -- `FAILED` - Reward failed (invalid wallet address, insufficient balance, etc.) - -### Rewards from platform account - -Get all rewards paid from your platform's USD internal account: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?senderAccountIdentifier=InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965&type=OUTGOING' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Combining filters - -Narrow down results by combining multiple filters: - -```bash -# All completed rewards for a customer in October -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?platformCustomerId=user_789&status=COMPLETED&startDate=2025-10-01T00:00:00Z&endDate=2025-10-31T23:59:59Z' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Pagination - -Handle large result sets with cursor-based pagination: - -```javascript -async function getAllRewardsForCustomer(platformCustomerId) { - const allRewards = []; - let cursor = null; - let hasMore = true; - - while (hasMore) { - const url = cursor - ? `https://api.lightspark.com/grid/2025-10-13/transactions?platformCustomerId=${platformCustomerId}&limit=100&cursor=${cursor}` - : `https://api.lightspark.com/grid/2025-10-13/transactions?platformCustomerId=${platformCustomerId}&limit=100`; - - const response = await fetch(url, { - headers: { Authorization: `Basic ${credentials}` }, - }); - - const { data, hasMore: more, nextCursor } = await response.json(); - - allRewards.push(...data); - hasMore = more; - cursor = nextCursor; - } - - return allRewards; -} -``` - -The maximum `limit` is 100 transactions per request. Default is 20. - -## Get a single transaction - -Retrieve details for a specific reward transaction: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000025' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -## Common use cases - -### Calculate total rewards distributed - -```javascript -async function calculateMonthlyRewards(month) { - const startDate = new Date(month); - const endDate = new Date(startDate); - endDate.setMonth(endDate.getMonth() + 1); - - let totalUSD = 0; - let totalBTC = 0; - let cursor = null; - - do { - const url = `/transactions?status=COMPLETED&startDate=${startDate.toISOString()}&endDate=${endDate.toISOString()}&cursor=${cursor || ''}`; - - const { data, nextCursor } = await fetch(url).then(r => r.json()); - - data.forEach(tx => { - totalUSD += tx.sentAmount.amount; - totalBTC += tx.receivedAmount.amount; - }); - - cursor = nextCursor; - } while (cursor); - - return { - totalUSD: totalUSD / 100, // Convert cents to dollars - totalBTC: totalBTC / 100000000, // Convert sats to BTC - }; -} -``` - -### Track customer reward history - -```javascript -async function getCustomerRewardsSummary(platformCustomerId) { - const response = await fetch( - `/transactions?platformCustomerId=${platformCustomerId}&status=COMPLETED`, - { headers: { Authorization: `Basic ${credentials}` } } - ); - - const { data, totalCount } = await response.json(); - - const totalSatsReceived = data.reduce( - (sum, tx) => sum + tx.receivedAmount.amount, - 0 - ); - - return { - rewardCount: totalCount, - totalSatsReceived, - lastRewardAt: data[0]?.settledAt, - }; -} -``` - -### Monitor failed rewards - -```javascript -async function getFailedRewards(startDate) { - const response = await fetch( - `/transactions?status=FAILED&type=OUTGOING&startDate=${startDate}`, - { headers: { Authorization: `Basic ${credentials}` } } - ); - - const { data } = await response.json(); - - return data.map(tx => ({ - transactionId: tx.id, - customerId: tx.platformCustomerId, - amount: tx.sentAmount.amount / 100, - failedAt: tx.createdAt, - description: tx.description, - })); -} -``` - -## Best practices - - -Use pagination when fetching transaction history to avoid timeouts and memory issues. - - - -Filter by `platformCustomerId` for easier reconciliation with your internal user IDs. - - - -Cache completed transaction data since it won't change after settlement. - - - -Always filter by `type=OUTGOING` when tracking rewards to exclude incoming transactions. - - -## Next steps - - - - Learn how to create and execute Bitcoin reward payouts - - - - Set up webhook handling for real-time transaction notifications - - - - View complete transaction API documentation - - diff --git a/mintlify/rewards/developer-guides/plaid.mdx b/mintlify/rewards/developer-guides/plaid.mdx deleted file mode 100644 index 19ab79f6..00000000 --- a/mintlify/rewards/developer-guides/plaid.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "External Accounts with Plaid" -description: "Simplify bank account verification with Plaid Link for external account setup" ---- - -import PlaidIntegration from '/snippets/plaid-integration.mdx'; - - - -## Next steps - - - -Learn more about managing external accounts - - - - Set up webhook handling for account notifications - - - -View complete Plaid API documentation - - diff --git a/mintlify/rewards/developer-guides/platform-configuration.mdx b/mintlify/rewards/developer-guides/platform-configuration.mdx deleted file mode 100644 index 88736911..00000000 --- a/mintlify/rewards/developer-guides/platform-configuration.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Platform Configuration" -description: "Configuring platform settings for rewards" ---- -import PlatformConfigAPI from '/snippets/platform-config-currency-api-webhooks.mdx'; - - \ No newline at end of file diff --git a/mintlify/rewards/index.mdx b/mintlify/rewards/index.mdx deleted file mode 100644 index c12556be..00000000 --- a/mintlify/rewards/index.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: 'Introduction' -description: 'Send instant Bitcoin rewards to users worldwide through a single API' ---- -import { rewardsProductName, topLevelProductName } from '/snippets/variables.mdx'; - -With {rewardsProductName}, you can send instant Bitcoin rewards to your **users' self-custody wallets** worldwide through a single, simple API. {topLevelProductName} automatically handles the entire process, managing the fiat-to-crypto conversion, compliance checks, and instant delivery for you. - - - - The Grid API combines USD-to-BTC conversion and payout into a single, atomic operation, simplifying the process of distributing rewards. - - - Grid converts your platform's fiat balance into Bitcoin on demand, allowing you to offer crypto rewards without managing digital asset custody or complex exchange integrations. - - - Delivers Bitcoin to your users’ wallets in seconds (like a Spark wallet) giving them immediate ownership and control. - - - ---- - -## Rewards Payout Flow - - - - Your platform's internal account is pre-funded with fiat currency (e.g., USD) via standard payment rails like ACH push. - - - For rewards, the only entity who needs to be KYB'd is the entity paying for the reward. This can be you, the platform, or - your business customers that want to pay out rewards to their end users. - - - You execute a single API call to create a quote, instantly convert a specific USD amount to BTC at the current market rate, and transfer the Bitcoin to the user's wallet address. - - - ---- - -## {rewardsProductName} Features - -Users interact with {rewardsProductName} through two main interfaces: - - - - Programmatic access to onboard customers, fund your platform account, get quotes for Bitcoin purchases, and execute reward payouts. Reconcile all activity with real-time webhooks. - - - Your development and operations team can use the dashboard to monitor balances and transactions, manage API keys and environments, and troubleshoot with detailed logs. - - - -### Onboarding Customers - -For rewards with Grid, the only entity who needs to be KYB'd is the entity paying for the reward. This can be you, the platform, or -your business customers that want to pay out rewards to their end users. -All you need in order to pay out a reward is the wallet address. -No need to collect extra personal information or go through the full hosted KYC flow for end users! -To generate a spark wallet, you can use a tool like [Privy](https://privy.io) or the Spark SDK directly. - -If you do have business customers that want to pay out rewards to their end users, you can onboard that business customer -via the hosted KYB link flow. - -### Funding Your Platform Account - -{rewardsProductName} operates on a pre-funded model. You can fund your internal platform account using several payment rails such as ACH, wire transfers, Lightning, and more. This stored balance is then used to instantly purchase and send Bitcoin rewards to your customers. - -### Sending Rewards - -To send a reward with {rewardsProductName}, you create and execute a quote. The API call specifies your funded internal account as the source and the customer's Bitcoin wallet address as the destination. Grid handles the USD-to-BTC conversion and instant delivery to the receiving wallet, notifying you of the completed transfer via webhook. - -### Environments - - -{rewardsProductName} supports two environments: **Sandbox** and **Production**. - -The Sandbox mirrors production behavior, allowing you to test the full end-to-end flow—from funding a test account and onboarding a mock customer to sending a simulated Bitcoin reward—without moving real funds. - -The Production environment uses live credentials and base URLs for real transactions once you're ready to launch. - - ---- - - - Ready to integrate {rewardsProductName}? Check out our quickstart guide. - diff --git a/mintlify/rewards/platform-tools/postman-collection.mdx b/mintlify/rewards/platform-tools/postman-collection.mdx deleted file mode 100644 index d11e88a6..00000000 --- a/mintlify/rewards/platform-tools/postman-collection.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Postman Collection" ---- - -import PostmanCollection from '/snippets/postman-collection.mdx'; - - - - diff --git a/mintlify/rewards/platform-tools/sandbox-testing.mdx b/mintlify/rewards/platform-tools/sandbox-testing.mdx deleted file mode 100644 index 8a050533..00000000 --- a/mintlify/rewards/platform-tools/sandbox-testing.mdx +++ /dev/null @@ -1,342 +0,0 @@ ---- -title: "Sandbox Testing" -description: "Test your rewards integration in the Grid sandbox environment" ---- - -## Overview - -The Grid sandbox environment allows you to test your rewards integration without moving real money or cryptocurrency. All API endpoints work the same way in sandbox as they do in production, but transactions are simulated and you can control test scenarios using special test values. - -## Getting Started with Sandbox - -### Sandbox Credentials - -To use the sandbox environment: - -1. Contact Lightspark to get your inital sandbox credentials configured. Email [support@lightspark.com](mailto:support@lightspark.com) to get started. -2. Add your sandbox API token and secret to your environment variables. -3. Use the normal production base URL: `https://api.lightspark.com/grid/2025-10-13` -4. Authenticate using your sandbox token with HTTP Basic Auth - -## Simulating Money Movements - -### Funding Platform Internal Accounts - -In production, your platform's internal account is funded by following the payment instructions (bank transfer, wire, etc.). In sandbox, you can instantly add funds to your platform's internal account using the following endpoint: - -```bash -POST /sandbox/internal-accounts/{accountId}/fund - -{ - "amount": 200000 # $2,000 in cents -} -``` - -**Example:** - -```bash -curl -X POST https://api.lightspark.com/grid/2025-10-13/sandbox/internal-accounts/InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965/fund \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "amount": 200000 - }' -``` - -This endpoint returns the updated `InternalAccount` object with the new balance. You'll also receive an `ACCOUNT_STATUS` webhook showing the balance change. - - - In production, ACH transfers typically take 1-3 business days to settle. In sandbox, funding is instant. - - -## Testing Reward Distributions - -### Testing Successful Bitcoin Rewards - -The standard reward flow works seamlessly in sandbox. Create and execute a quote to instantly convert USD to BTC and send to a Spark wallet: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true, - "description": "Bitcoin reward payout!" - }' -``` - -In sandbox: -- The USD is instantly debited from your platform's internal account -- Bitcoin is "purchased" at a simulated exchange rate -- The Bitcoin is delivered to the Spark wallet address. In sandbox, BTC funds are regtest funds so that they're compatible with real regtest spark wallets. -- You receive an `OUTGOING_PAYMENT` webhook notification - - - In sandbox, Bitcoin transfers complete instantly on regtest. In production, Spark wallet transfers typically complete within seconds. - - -### Testing Wallet Address Failures - -Use special Spark wallet address patterns to test different failure scenarios. The **last 3 digits** of the wallet address determine the test behavior: - -| Last Digits | Behavior | Use Case | -|-------------|----------|----------| -| **003** | Wallet unavailable | Recipient wallet is offline or unreachable | -| **005** | Timeout/delayed failure | Transaction stays pending ~30s, then fails | -| **Any other** | Success | All transfers complete normally | - -**Example - Testing Wallet Unavailable:** - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6m003" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true - }' -``` - -The quote execution will fail immediately with a wallet unavailable error. - -Note that these failure test patterns work for any external account type. If you want to test other cases of funding from a broken fiat account, -you can create an external account with the appropriate test pattern and use that for the quote source for funding. There are also two other -failure test patterns relevant for bank accounts: - -- **002**: Insufficient funds (transfer-in will fail) -- **004**: Transfer rejected (bank rejects the transfer) - -## Testing Customer Onboarding - -### Sandbox KYB Flow - -In sandbox, the KYB onboarding process is simplified to always use the `/customers` endpoint instead of the KYB link flow. - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "platformCustomerId": "user_12345", - "customerType": "INDIVIDUAL", - "fullName": "Jane Doe", - "birthDate": "1992-03-25", - "nationality": "US" - }' -``` - -In sandbox, customers are automatically approved. In production, KYB verification may take several minutes. - -## Testing Insufficient Balance - -To test insufficient balance scenarios, simply attempt to send more than your platform's internal account balance: - -```bash -# Assuming account balance is $2,000 (200000 cents) -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 300000, - "immediatelyExecute": true - }' -``` - -The quote execution will fail with an insufficient balance error. - -## Testing Webhooks - -All webhook events fire normally in sandbox. To test your webhook endpoint: - -1. Configure your webhook URL in the dashboard -2. Perform actions that trigger webhooks (funding accounts, executing quotes, etc.) -3. Receive webhook events at your endpoint -4. Verify signature using the sandbox public key - -You can also manually trigger a test webhook: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/webhooks/test" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "url": "https://your-app.com/webhooks" - }' -``` - -## Common Testing Workflows - -### Complete Reward Distribution Test - -Here's a complete test workflow for distributing a $1.00 Bitcoin reward: - -1. **Fund your platform's internal account:** - ```bash - POST /sandbox/internal-accounts/InternalAccount:platform-usd/fund - { "amount": 100000 } # $1,000 - ``` - -2. **Create a test customer:** - ```bash - POST /customers - ``` - -3. **Execute a reward quote:** - ```bash - POST /quotes - # Platform USD account → Customer's Spark wallet - # With immediatelyExecute: true - ``` - -4. **Verify completion via webhook** (`OUTGOING_PAYMENT` event) - -5. **Check transaction history:** - ```bash - GET /transactions?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001 - ``` - -### Testing Error Scenarios - -Test each failure mode systematically: - -```bash -# 1. Test wallet unavailable (003) -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "source": {"accountId": "InternalAccount:platform-usd"}, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:test001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6m003" # Wallet unavailable *003 - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true - }' -# Response: Transaction fails with wallet unavailable error - -# 2. Test insufficient balance -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "source": {"accountId": "InternalAccount:platform-usd"}, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:test001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000000, - "immediatelyExecute": true - }' -# Response: 400 Bad Request with insufficient balance error - -# 3. Test timeout scenario (005) -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "sandbox_token_id:sandbox_token_secret" \ - -H "Content-Type: application/json" \ - -d '{ - "source": {"accountId": "InternalAccount:platform-usd"}, - "destination": { - "externalAccountDetails": { - "customerId": "Customer:test001", - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6m005" # Timeout/delayed failure *005 - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true - }' -# Check status immediately - will show PENDING -# Wait 30s, check again - will show FAILED -``` - -## Sandbox Limitations - -While sandbox closely mimics production, there are some differences: - -- **Instant settlement**: All Bitcoin transfers complete instantly (success cases) or fail immediately (error cases), except timeout scenarios (005) -- **Uses Regtest funds**: Spark bitcoin funds are regtest funds so that they're compatible with real regtest spark wallets. -- **Simplified KYB**: KYB processes are simulated and complete instantly with automatic approval -- **Fixed exchange rates**: Currency conversion rates may not reflect real-time market rates - - -Do not try sending money to any sandbox wallet addresses or bank accounts. These are not real addresses and will not receive funds. - - -## Moving to Production - -When you're ready to move to production: - -1. Generate production API tokens in the dashboard -2. Swap those credentials for the sandbox credentials in your environment variables -3. Remove any sandbox-specific test patterns from your code (magic number wallet addresses) -4. Configure production webhook endpoints -5. Test with small reward amounts first ($0.01-$1.00) -6. Gradually increase volume as you gain confidence - -## Next Steps - -- Review [Webhooks](/rewards/platform-tools/webhooks) for event handling -- Check out the [Postman Collection](/rewards/platform-tools/postman-collection) for API examples -- See [Platform Configuration](/rewards/developer-guides/platform-configuration) for production settings diff --git a/mintlify/rewards/platform-tools/webhooks.mdx b/mintlify/rewards/platform-tools/webhooks.mdx deleted file mode 100644 index e636c6ff..00000000 --- a/mintlify/rewards/platform-tools/webhooks.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Webhooks" ---- - -import Webhooks from '/snippets/webhooks.mdx'; - - \ No newline at end of file diff --git a/mintlify/rewards/quickstart.mdx b/mintlify/rewards/quickstart.mdx deleted file mode 100644 index 4f76ccbc..00000000 --- a/mintlify/rewards/quickstart.mdx +++ /dev/null @@ -1,323 +0,0 @@ ---- -title: "Quickstart" -description: "Complete walkthrough for buying Bitcoin and sending it as a reward to an external Spark wallet for self-custody" ---- - -This guide walks you through the complete process of buying Bitcoin with USD and sending it to a self-custody Spark wallet, from customer onboarding through quote execution. - -## Understanding Entity Mapping for Rewards - -In this guide, the entities map as follows (platform-funded model): - -| Entity Type | Who They Are | In This Example | -|------------|--------------|----------------| -| **Platform** | Your rewards app paying rewards directly | Your cashback/rewards platform | -| **Customer** | (Not used in this model) | N/A | -| **External Account** | Users' crypto wallets receiving rewards | User's self-custody Spark wallet | - -**Flow**: Your platform funds its internal account → sends Bitcoin micro-payouts directly → to users' external crypto wallets at scale. This is common for cashback apps where you earn affiliate commissions and share them with users. - - -For white-label reward programs where brands like Nike or Starbucks fund their own reward campaigns, those brands would be created as **Customers** who manage their own reward budgets. - - -## Prerequisites - -Before starting this guide, ensure you have: - -- A Grid API account with valid authentication credentials -- Access to the Grid API endpoints (production or sandbox) -- A webhook endpoint configured to receive notifications -- A Spark wallet address where the Bitcoin will be sent - -## Overview - -The process consists of the following steps: - -1. **List platform internal accounts** to find your platform's USD funding instructions -2. **Fund your internal account** via ACH push and receive a webhook notification -3. **Generate a spark wallet** for your customer, or let them connect their own -4. **Execute a quote** to complete the Bitcoin purchase and transfer a reward to the user's own Spark wallet. - -## Step 1: List your platform's internal accounts - -When your platform is first created, it is automatically assigned an internal account with a balance in your configured fiat currency. -List your platform's internal accounts to see the available balances and funding instructions for USD. - -### Request - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/platform/internal-accounts?currency=USD" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -### Response - -```json -{ - "data": [ - { - "id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "balance": { - "amount": 0, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "fundingPaymentInstructions": [ - { - "instructionsNotes": "Include the reference code in your ACH transfer memo", - "accountOrWalletInfo": { - "accountType": "US_ACCOUNT", - "reference": "FUND-BTC123", - "accountNumber": "9876543210", - "routingNumber": "021000021", - "accountHolderName": "Lightspark Payments FBO John Doe", - "bankName": "JP Morgan Chase" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SPARK_WALLET", - "assetType": "USDB", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu", - "invoice": "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ], - "createdAt": "2025-10-03T12:00:00Z", - "updatedAt": "2025-10-03T12:00:00Z" - } - ] -} -``` - -The `fundingPaymentInstructions` provide the bank account details and reference code needed to fund this internal account via ACH pull from the customer's bank. - - - You can also see that there are Spark wallet funding instructions in this - example response which can be used to fund the internal account with USDB - instantly. - - -## Step 2: Fund your Internal Account - -You can initiate an ACH transfer from your bank to the account details provided in the funding instructions, making sure to include the reference code `FUND-BTC123` in the transfer memo. - - - In sandbox mode, you can use the `/sandbox/internal-accounts/{accountId}/fund` - endpoint to simulate receiving funds. In production, actual ACH transfers - typically take 1-3 business days to settle. - - -### Webhook Notification - -When the funds are received and the internal account balance is updated, you'll receive a webhook notification: - -```json -{ - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "oldBalance": { - "amount": 0, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "newBalance": { - "amount": 200000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "timestamp": "2025-10-03T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "ACCOUNT_STATUS" -} -``` - -The internal account now has a balance of $2,000.00 (200000 cents). You can use this balance to instantly distribute Bitcoin rewards to your customers. - -## Step 3: Customer Onboarding - -This guide assumes you have a Spark wallet address for your customer who will receive the Bitcoin reward. -For rewards, the only entity who needs to be KYB'd is the entity paying for the reward - in this case, you, the platform! -All you need in order to pay out a reward is the wallet address. No need to go through the full hosted KYC flow -for this use case! To generate a spark wallet, you can use a tool like [Privy](https://privy.io) or the Spark SDK directly. - -## Step 4: Create and Execute a Quote to the Customer's Spark Wallet - -Create and execute a trade from USD to BTC, and initiate the final transfer in one step. This combines external account -creation and quote execution using the `externalAccountDetails` option. - -### Request - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" - }, - "destination": { - "externalAccountDetails": { - "currency": "BTC", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 100, - "immediatelyExecute": true, - "description": "Bitcoin reward payout!" - }' -``` - - - **Combined External Account Creation and Quote Execution**: The `externalAccountDetails` option allows you to - create the external account and execute the quote in a single API call, which is perfect for one-off payments - to new destinations. The external account will be automatically created and then used as the destination for - the Bitcoin transfer. Its ID in the response can be used directly in future quote creation requests. - -**Immediate Quote Execution (Market Order)**: Note that `immediatelyExecute` is set to `true` in this example. -Because we always just want to send $1.00 worth of BTC to users as a reward at the current market rate, we don't -need to lock a quote and view the rate details before executing. If you want to lock a quote and confirm fees -and exchange rate details before executing the quote, set `immediatelyExecute` to `false` or omit the field. - - - -### Response - -```json -{ - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020", - "status": "PROCESSING", - "createdAt": "2025-10-03T15:00:00Z", - "expiresAt": "2025-10-03T15:05:00Z", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:b23dcbd6-dced-4ec4-b756-3c3a9ea3d456", - "currency": "BTC" - }, - "sendingCurrency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - }, - "receivingCurrency": { - "code": "BTC", - "name": "Bitcoin", - "symbol": "₿", - "decimals": 8 - }, - "totalSendingAmount": 100, - "totalReceivingAmount": 810, - "exchangeRate": 8.1, - "feesIncluded": 5, - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000025" -} -``` - -The quote shows: - -- **Sending**: \$1.00 USD (including $0.05 fee) -- **Receiving**: 0.0000081 BTC (810 satoshis) -- **Exchange rate**: 8.1 sats per USD cent (~$123,000 per BTC) -- **External account created**: The Spark wallet was automatically added as an external account during quote creation - -The quote status changes to `PROCESSING` and the Bitcoin transfer is initiated. The external account is created, USD is debited from the internal account, Bitcoin is purchased, and then sent to the Spark wallet address. - -You can track the status by: - -1. Polling the quote endpoint: `GET /quotes/{quoteId}` -2. Waiting for a webhook notification - -### Completion Webhook - -When the Bitcoin transfer completes, you'll receive a webhook notification: - -```json -{ - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000025", - "status": "COMPLETED", - "type": "OUTGOING", - "sentAmount": { - "amount": 100, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "receivedAmount": { - "amount": 810, - "currency": { - "code": "BTC", - "name": "Bitcoin", - "symbol": "₿", - "decimals": 8 - } - }, - "settledAt": "2025-10-03T15:01:45Z", - "createdAt": "2025-10-03T15:00:00Z", - "description": "Bitcoin purchase for self-custody", - "exchangeRate": 8.1, - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000020" - }, - "timestamp": "2025-10-03T15:02:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000030", - "type": "OUTGOING_PAYMENT" -} -``` - - - Bitcoin transfers to Spark wallets typically complete within seconds, much - faster than traditional Bitcoin on-chain transactions. - - -## Summary - -You've successfully completed a Bitcoin purchase and transfer to a self-custody Spark wallet! Here's what happened: - -1. ✅ Listed internal accounts and obtained USD funding instructions -2. ✅ Funded the internal account with USD via ACH -3. ✅ Generated a Spark wallet for the customer -4. ✅ Created and executed a quote to purchase Bitcoin and send to the Spark wallet - -The customer now has 810 Satoshis in their self-custody Spark wallet! - -## Next Steps - -- **Transaction history**: Use `GET /transactions` to track all Bitcoin purchases -- **Price monitoring**: Build price alerts using the lookup endpoint to monitor rates -- **Webhook verification**: Implement signature verification for webhook security (see [Webhooks guide](/rewards/platform-tools/webhooks)) - -## Related Resources - -- [API Reference](/api-reference) - Complete API documentation -- [Platform Configuration](/rewards/developer-guides/platform-configuration) - Configure your platform settings -- [Webhooks](/rewards/platform-tools/webhooks) - Webhook security and verification diff --git a/mintlify/rewards/terminology.mdx b/mintlify/rewards/terminology.mdx deleted file mode 100644 index 8e1a67ab..00000000 --- a/mintlify/rewards/terminology.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Core Concepts" -description: "Core concepts and terminology for the Grid API" ---- - -import Terminology from '/snippets/terminology.mdx'; - - - - diff --git a/mintlify/snippets/country-support.mdx b/mintlify/snippets/country-support.mdx deleted file mode 100644 index b75467ec..00000000 --- a/mintlify/snippets/country-support.mdx +++ /dev/null @@ -1,99 +0,0 @@ - - - **Available Globally** -Bitcoin (BTC) and Stablecoin transactions supported worldwide with no geographic restrictions. - - - **United States & Europe** -Onboard as a platform with complete access to APIs, hosted KYC/KYB, dashboard, and business integrations. -Send payments to 65 countries on local banking rails, in addition to BTC and stablecoins. - - - **65 Countries** -Receive payments via local rails like SEPA, PIX, UPI, and more. - - - -| Country | ISO Code | Payment Rails | -|---|:---:|:---:| -| 🇦🇹 Austria | AT | `SEPA` `SEPA Instant` | -| 🇧🇪 Belgium | BE | `SEPA` `SEPA Instant` | -| 🇧🇯 Benin | BJ | `Bank Transfer` | -| 🇧🇼 Botswana | BW | `Bank Transfer` | -| 🇧🇷 Brazil | BR | `PIX` | -| 🇧🇬 Bulgaria | BG | `SEPA` `SEPA Instant` | -| 🇧🇫 Burkina Faso | BF | `Bank Transfer` | -| 🇨🇲 Cameroon | CM | `Bank Transfer` | -| 🇨🇦 Canada | CA | `Bank Transfer` | -| 🇨🇳 China | CN | `Bank Transfer` | -| 🇨🇷 Costa Rica | CR | `Bank Transfer` | -| 🇭🇷 Croatia | HR | `SEPA` `SEPA Instant` | -| 🇨🇾 Cyprus | CY | `SEPA` `SEPA Instant` | -| 🇨🇿 Czech Republic | CZ | `SEPA` `SEPA Instant` | -| 🇩🇰 Denmark | DK | `SEPA` `SEPA Instant` | -| 🇨🇩 DR Congo | CD | `Bank Transfer` | -| 🇪🇪 Estonia | EE | `SEPA` `SEPA Instant` | -| 🇫🇮 Finland | FI | `SEPA` `SEPA Instant` | -| 🇫🇷 France | FR | `SEPA` `SEPA Instant` | -| 🇩🇪 Germany | DE | `SEPA` `SEPA Instant` | -| 🇬🇭 Ghana | GH | `Bank Transfer` | -| 🇬🇷 Greece | GR | `SEPA` `SEPA Instant` | -| 🇭🇰 Hong Kong | HK | `Bank Transfer` | -| 🇭🇺 Hungary | HU | `SEPA` `SEPA Instant` | -| 🇮🇸 Iceland | IS | `SEPA` `SEPA Instant` | -| 🇮🇳 India | IN | `UPI` `IMPS` | -| 🇮🇩 Indonesia | ID | `Bank Transfer` | -| 🇮🇪 Ireland | IE | `SEPA` `SEPA Instant` | -| 🇮🇹 Italy | IT | `SEPA` `SEPA Instant` | -| 🇨🇮 Ivory Coast | CI | `Bank Transfer` | -| 🇰🇪 Kenya | KE | `Bank Transfer` | -| 🇱🇻 Latvia | LV | `SEPA` `SEPA Instant` | -| 🇱🇮 Liechtenstein | LI | `SEPA` `SEPA Instant` | -| 🇱🇹 Lithuania | LT | `SEPA` `SEPA Instant` | -| 🇱🇺 Luxembourg | LU | `SEPA` `SEPA Instant` | -| 🇲🇼 Malawi | MW | `Bank Transfer` | -| 🇲🇾 Malaysia | MY | `Bank Transfer` | -| 🇲🇱 Mali | ML | `Bank Transfer` | -| 🇲🇹 Malta | MT | `SEPA` `SEPA Instant` | -| 🇲🇽 Mexico | MX | `SPEI` | -| 🇳🇱 Netherlands | NL | `SEPA` `SEPA Instant` | -| 🇳🇬 Nigeria | NG | `Bank Transfer` | -| 🇳🇴 Norway | NO | `SEPA` `SEPA Instant` | -| 🇵🇭 Philippines | PH | `Bank Transfer` | -| 🇵🇱 Poland | PL | `SEPA` `SEPA Instant` | -| 🇵🇹 Portugal | PT | `SEPA` `SEPA Instant` | -| 🇷🇴 Romania | RO | `SEPA` `SEPA Instant` | -| 🇸🇳 Senegal | SN | `Bank Transfer` | -| 🇸🇬 Singapore | SG | `PayNow` `FAST` `Bank Transfer` | -| 🇸🇰 Slovakia | SK | `SEPA` `SEPA Instant` | -| 🇸🇮 Slovenia | SI | `SEPA` `SEPA Instant` | -| 🇿🇦 South Africa | ZA | `Bank Transfer` | -| 🇰🇷 South Korea | KR | `Bank Transfer` | -| 🇪🇸 Spain | ES | `SEPA` `SEPA Instant` | -| 🇱🇰 Sri Lanka | LK | `Bank Transfer` | -| 🇸🇪 Sweden | SE | `SEPA` `SEPA Instant` | -| 🇨🇭 Switzerland | CH | `SEPA` `SEPA Instant` | -| 🇹🇿 Tanzania | TZ | `Bank Transfer` | -| 🇹🇭 Thailand | TH | `Bank Transfer` | -| 🇹🇬 Togo | TG | `Bank Transfer` | -| 🇺🇬 Uganda | UG | `Bank Transfer` | -| 🇬🇧 United Kingdom | GB | `Faster Payments` `Bank Transfer` | -| 🇺🇸 United States | US | `ACH` `Wire Transfer` `RTP` `FedNow` | -| 🇻🇳 Vietnam | VN | `Bank Transfer` | -| 🇿🇲 Zambia | ZM | `Bank Transfer` | - -Regional Summary - - - **32 countries** Primary: SEPA/SEPA Instant - - - **17 countries** Primary: Bank Transfer - - - **11 countries** Various instant payment systems - - - **5 countries** PIX, SPEI, ACH, FedNow - - diff --git a/mintlify/snippets/creating-customers/customer-types.mdx b/mintlify/snippets/creating-customers/customer-types.mdx deleted file mode 100644 index 0d4ac2c1..00000000 --- a/mintlify/snippets/creating-customers/customer-types.mdx +++ /dev/null @@ -1,5 +0,0 @@ -## Customer Types - -The Grid API supports both individual and business customers. While the API schema itself makes most Personally Identifiable Information (PII) optional at initial creation, specific fields may become mandatory based on the currencies the customer will transact with. - -Your platform’s configuration ( retrieved via `GET /config`) includes a supportedCurrencies array. Each currency object within this array has a providerRequiredCustomerFields list. If a customer is intended to use a specific currency, any fields listed for that currency must be provided when creating or updating the customer. \ No newline at end of file diff --git a/mintlify/snippets/creating-customers/customers.mdx b/mintlify/snippets/creating-customers/customers.mdx deleted file mode 100644 index ede8bb81..00000000 --- a/mintlify/snippets/creating-customers/customers.mdx +++ /dev/null @@ -1,303 +0,0 @@ -export const Customers = ({ individualEnabled = true, businessEnabled = true, umaEnabled = true }) => { - return ( - <> - This guide provides comprehensive information about creating customers in the Grid API, including customer types, onboarding models, registration, and management. - - ## Onboarding model - - There are two platform models for regulated and unregulated platforms. - - - Regulated institutions: Use your existing compliance processes. Create individual and business customers directly via `POST /customers`. The information you supply is used for beneficiary/counterparty compliance screening. - - Unregulated institutions: Grid performs KYC/KYB. Generate a hosted KYC/KYB link, redirect your customer to complete verification in their locale, receive a KYC result webhook. While KYC is pending, allow customers to finish account setup but block funding and money movement. - - ## Customer Types - - The Grid API supports both individual and business customers. While the API schema itself makes most Personally Identifiable Information (PII) optional at initial creation, specific fields may become mandatory based on the currencies the customer will transact with. - - Your platform's configuration (retrieved via `GET /config`) includes a `supportedCurrencies` array. Each currency object within this array has a `providerRequiredCustomerFields` list. If a customer is intended to use a specific currency, any fields listed for that currency **must** be provided when creating or updating the customer. - - The base required information for all customers is only: - - - Platform customer ID (your internal identifier) - - Customer type (`INDIVIDUAL` or `BUSINESS`) - - If using sending and receiving to just-in-time UMA addresses, you'll also need to specify the bank account information - - ### Individual customers - - In some cases, only the above fields are required at customer creation. Beyond those base requirements, additional fields commonly associated with individual customers include: - - - Full name - - Date of birth (YYYY-MM-DD format) - - Physical address (including country, state, city, postalCode) - - **Note:** Check the `providerRequiredCustomerFields` for each relevant currency in your platform's configuration to determine which of these fields are strictly mandatory at creation/update time for that customer to transact in those currencies. - - ### Business customers - - Beyond the base requirements, additional fields commonly associated with business customers include: - - - Business information: - - Legal name (this is often required, check `providerRequiredCustomerFields`) - - Registration number (optional, unless specified by `providerRequiredCustomerFields`) - - Tax ID (optional, unless specified by `providerRequiredCustomerFields`) - - Physical address (including country, state, city, postalCode) - - **Note:** Check the `providerRequiredCustomerFields` for each relevant currency in your platform's configuration to determine which of these fields are strictly mandatory at creation/update time for that customer to transact in those currencies. - - When creating or updating customers, the `customerType` field must be specified as either `INDIVIDUAL` or `BUSINESS`. - - There can be multiple customers with the same platformCustomerId but different UMA addresses. This is useful if you want to track multiple UMA addresses and/or bank accounts for the same customer in your platform. - - -## Customer Creation Process - - ### Creating a new individual customer (regulated institutions) - -To register a new customer directly, use the `POST /customers` endpoint (regulated institutions): - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{{ - "platformCustomerId": "9f84e0c2a72c4fa", - "customerType": "INDIVIDUAL", - "fullName": "Jane Doe", - }}' -``` - -The API allows creating a customer with minimal PII. However, to enable transactions for a customer in specific currencies, you must include any PII fields mandated by the `providerRequiredCustomerFields` for those currencies (found in your platform's configuration via `GET /config`). - - -The examples below show a more comprehensive set of data. Not all fields are strictly required by the API for customer creation itself, but become necessary based on currency and provider requirements. - -Example request body for an individual customer with UMA instant payments enabled (ensure all `providerRequiredCustomerFields` for intended currencies are included): - -Typically bank account information is provided separately via internal and external account management. However, when using UMA for instant payments, since funding and withdrawals are instant, bank account information can be provided at time of customer creation. - - -```json -{{ - "umaAddress": "$john.sender@mycompany.com", - "platformCustomerId": "9f84e0c2a72c4fa", - "customerType": "INDIVIDUAL", - "fullName": "John Sender", - "birthDate": "1985-06-15", - "address": { - "line1": "Paseo de la Reforma 222", - "line2": "Piso 15", - "city": "Ciudad de México", - "state": "Ciudad de México", - "postalCode": "06600", - "country": "MX" - } -}} -``` - -UMA addresses follow the format `$username@domain`. For your platform: - -1. The `domain` part will be your configured UMA domain (set in platform configuration) -2. The `username` part can be chosen by you or your customers, following these rules: - - Must start with a $ symbol. This is to differentiate from email addresses and clearly identify an uma address. - - The `username` portion is limited to a-z0-9-_.+ - - Addresses are case-insensitive, but by convention are written only with lowercase letters - - Like email addresses, the maximum number of characters for the `username` portion of the address is 64 characters (including the $). - -The Grid API validates these requirements and will return an error if they are not met. - - -### Creating a new business customer (regulated institutions) - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{{ - "umaAddress": "$acme.corp@mycompany.com", - "platformCustomerId": "b87d2e4a9c13f5b", - "customerType": "BUSINESS", - "businessInfo": { - "legalName": "Acme Corporation", - "registrationNumber": "789012345", - "taxId": "123-45-6789" - }, - "address": { - "line1": "456 Oak Avenue", - "line2": "Floor 12", - "city": "New York", - "state": "NY", - "postalCode": "10001", - "country": "US" - } - }}' -``` - -### Onboarding customers (unregulated institutions) - -Unregulated institutions should initiate a hosted KYC/KYB flow. Generate a link and redirect the customer to complete verification. While KYC is pending, allow account setup but block funding and money movement. - -1. Request a hosted KYC link for a customer using your `platformCustomerId` (optional `redirectUri` to return the customer to your app when finished): - -```bash -curl -sS -G "https://api.lightspark.com/grid/2025-10-13/customers/kyc-link" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - --data-urlencode "platformCustomerId=9f84e0c2a72c4fa" \ - --data-urlencode "redirectUri=https://app.example.com/onboarding/completed" -``` - -Response: -```json -{{ - "kycUrl": "https://kyc.grid.example/onboard/abc123", - "platformCustomerId": "9f84e0c2a72c4fa", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001" -}} -``` - -2. Redirect the customer to `kycUrl` to complete KYC/KYB in their locale. -3. After the customer is redirected back to your app, they can continue with account setup until KYC review is complete. -4. Handle the KYC status webhook. Grid notifies you when a decision is reached; update your records and unlock funding on APPROVED. - -Webhook example: -```json -{{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "kycStatus": "APPROVED", - "type": "KYC_STATUS", - "timestamp": "2025-01-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007" -}} -``` - - -Possible final statuses include APPROVED, REJECTED, EXPIRED, CANCELED, MANUALLY_APPROVED, and MANUALLY_REJECTED. Verify `X-Grid-Signature` against the raw request body using the dashboard public key. - - - -## Customer management - -### Retrieving customer information - -You can retrieve customer information using either the Grid-assigned customer ID or your platform's customer ID: - -```bash -curl -sS -X GET "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -or list customers with a filter: - -```bash -curl -sS -G "https://api.lightspark.com/grid/2025-10-13/customers" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - --data-urlencode "umaAddress={umaAddress}" \ - --data-urlencode "platformCustomerId={platformCustomerId}" \ - --data-urlencode "customerType={customerType}" \ - --data-urlencode "createdAfter={createdAfter}" \ - --data-urlencode "createdBefore={createdBefore}" \ - --data-urlencode "cursor={cursor}" \ - --data-urlencode "limit={limit}" -``` - -Note that this example shows all available filters. You can use any combination of them. - -### Updating customer information - -Use PATCH to update customer information: - -```bash -curl -sS -X PATCH "https://api.lightspark.com/grid/2025-10-13/customers/{customerId}" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -H "Content-Type: application/json" \ - -d '{{ - "umaAddress": "$john.doe@uma.domain.com", - }}' -``` - -## Data validation - -The Grid API performs validation on all customer data. Common validation rules include: - -- All required fields must be present based on customer type -- Date of birth must be in YYYY-MM-DD format and represent a valid date -- Names must not contain special characters or excessive spaces -- Bank account information must follow country-specific formats -- Addresses must include all required fields including country code - -If validation fails, the API will return a 400 Bad Request response with detailed error information. - -## Best practices - -1. **Regular updates**: Keep customer information up to date, especially banking details -1. **Error handling**: Implement proper error handling to manage validation failures gracefully -1. **Idempotent operations**: Use your platformCustomerId consistently to avoid duplicate customer creation - -## Bulk customer import operations - -For scenarios where you need to add many customers to the system at once, the API provides a CSV file upload endpoint. - -### CSV file upload - -For large-scale customer imports, you can upload a CSV file containing customer information: - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/customers/bulk/csv" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" \ - -F "file=@customers.csv" -``` - -The CSV file should follow a specific format with required and optional columns based on customer type. Here's an example: - -```csv -umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,accountType,accountNumber,bankName,platformAccountId,businessLegalName,routingNumber,accountCategory\ -$john.doe@uma.domain.com,cust_user123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US,US_ACCOUNT,123456789,Chase Bank,chase_primary_1234,,222888888,SAVINGS -$acme@uma.domain.com,cust_biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US,US_ACCOUNT,987654321,Bank of America,boa_business_5678,Acme Corp,121212121,CHECKING -``` - - -CSV Upload Best Practices - -1. Use a spreadsheet application to prepare your CSV file -2. Validate data before upload (e.g., date formats, required fields) -3. Include a header row with column names -4. Use UTF-8 encoding for special characters -5. Keep file size under 100MB for optimal processing - - -You can track the job status through: - -1. Webhook notifications (if configured) -2. Status polling endpoint: - -```bash -curl -sS -X GET "https://api.lightspark.com/grid/2025-10-13/customers/bulk/jobs/{jobId}" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -Example job status response: - -```json -{{ - "jobId": "job_123456789", - "status": "PROCESSING", - "progress": { - "total": 5000, - "processed": 2500, - "successful": 2499, - "failed": 1 - }, - "errors": [ - { - "platformCustomerId": "cust_biz456", - "error": { - "code": "validation_error", - "message": "Invalid bank account number" - } - } - ] -}} -``` - - ); -}; \ No newline at end of file diff --git a/mintlify/snippets/creating-customers/onboarding-model.mdx b/mintlify/snippets/creating-customers/onboarding-model.mdx deleted file mode 100644 index f97511fb..00000000 --- a/mintlify/snippets/creating-customers/onboarding-model.mdx +++ /dev/null @@ -1,6 +0,0 @@ -## Onboarding model - -There are two models for regulated and unregulated platforms. - -- Regulated platforms: Use your existing compliance processes. Create individual and business customers directly via `POST /customers`. The information you supply is used for beneficiary/counterparty compliance screening. -- Unregulated platforms: Grid performs KYC/KYB. Generate a hosted KYC/KYB link, redirect your customer to complete verification in their locale, receive a KYC result webhook. While KYC is pending, allow customers to finish account setup but block funding and money movement. \ No newline at end of file diff --git a/mintlify/snippets/depositing-funds.mdx b/mintlify/snippets/depositing-funds.mdx deleted file mode 100644 index a91056cf..00000000 --- a/mintlify/snippets/depositing-funds.mdx +++ /dev/null @@ -1,86 +0,0 @@ -Grid provides two options to fund an account: - -- Prefund -- Just-in-time funding - -With prefunding, you'll deposit funds into internal accounts via Wire, PIX, or crypto transfers. You can then use the balances as the source of funds for quotes and transfers. - -With just-in-time funding, you'll receive payment instructions as part of the quote. Once funds arrive, the payment to the receiver is automatically initiated. - - - Just-in-time funding supports instant payment rails only (for example: RTP, - PIX, SEPA Instant). - - -## Prerequisites - -- You have created a customer (for customer-scoped internal accounts) -- You have `GRID_CLIENT_ID` and `GRID_CLIENT_SECRET` - - -Export your credentials for use with cURL: - -```bash -export GRID_CLIENT_ID="your_client_id" -export GRID_CLIENT_SECRET="your_client_secret" -``` - - - -## Prefunding an account via push payments (Wire, SEPA, PIX, etc.) - -When customers need to deposit funds themselves, display the funding payment instructions in your application: - - - -Fetch the customer's internal account for their desired currency using the API. - - -```bash cURL (Customer accounts) -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -```bash cURL (Platform internal accounts) -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/platform/internal-accounts' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - - - - -Parse the `fundingPaymentInstructions` array and select the appropriate instructions based on your customer's preferred payment method. - -```javascript -const instructions = account.fundingPaymentInstructions[0]; -const bankInfo = instructions.accountOrWalletInfo; -``` - - - - -Show the payment details prominently in your UI and enable copy / paste: - -- Account holder name -- Bank name and routing information (account/routing number, CLABE, PIX key, etc.) -- Reference code (if provided) -- Any additional notes from `instructionsNotes` - - -Customer initiates a push payment from their bank or wallet to the account/address specified. - - -Set up webhook listeners to receive updates for the deposit transaction and account balance updates. The account balance will update automatically. - - -You'll receive `ACCOUNT_STATUS` webhook events when the internal account balance changes. - - - - -## Just-in-time funding (payment instructions from a quote) - -With just-in-time funding, you request a quote and receive payment instructions (for example, a bank account or instant rail details). When your customer confirms the transaction, you trigger payment from your app. - -More details of just-in-time funding can be found in the Sending Payments guides. \ No newline at end of file diff --git a/mintlify/snippets/error-handling.mdx b/mintlify/snippets/error-handling.mdx deleted file mode 100644 index df0cec47..00000000 --- a/mintlify/snippets/error-handling.mdx +++ /dev/null @@ -1,551 +0,0 @@ -Learn how to handle errors when working with payments and transactions in Grid. Proper error handling ensures a smooth user experience and helps you quickly identify and resolve issues. - -## HTTP status codes - -Grid uses standard HTTP status codes to indicate the success or failure of requests: - -| Status Code | Meaning | When It Occurs | -| --------------------------- | ----------------------- | ------------------------------------------------------- | -| `200 OK` | Success | Request completed successfully | -| `201 Created` | Resource created | New transaction, quote, or customer created | -| `202 Accepted` | Accepted for processing | Async operation initiated (e.g., bulk CSV upload) | -| `400 Bad Request` | Invalid input | Missing required fields or invalid parameters | -| `401 Unauthorized` | Authentication failed | Invalid or missing API credentials | -| `403 Forbidden` | Permission denied | Insufficient permissions or customer not ready | -| `404 Not Found` | Resource not found | Customer, transaction, or quote doesn't exist | -| `409 Conflict` | Resource conflict | Quote already executed, external account already exists | -| `412 Precondition Failed` | UMA version mismatch | Counterparty doesn't support required UMA version | -| `422 Unprocessable Entity` | Missing info | Additional counterparty information required | -| `424 Failed Dependency` | Counterparty issue | Problem with external UMA provider | -| `500 Internal Server Error` | Server error | Unexpected server issue (contact support) | -| `501 Not Implemented` | Not implemented | Feature not yet supported | - -## API error responses - -All error responses include a structured format: - -```json -{ - "status": 400, - "code": "INVALID_AMOUNT", - "message": "Amount must be greater than 0", - "details": { - "field": "amount", - "value": -100 - } -} -``` - -### Common error codes - - - -**Cause:** Missing required fields or invalid data format - -**Solution:** Check request parameters match API specification - -```javascript -// Error -{ - "status": 400, - "code": "INVALID_INPUT", - "message": "Invalid account ID format" -} - -// Fix: Ensure proper ID format -const accountId = "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965"; -``` - - - - -**Cause:** Attempting to execute an expired quote - -**Solution:** Create a new quote before executing - -```javascript -async function executeQuoteWithRetry(quoteId) { - try { - return await executeQuote(quoteId); - } catch (error) { - if (error.code === "QUOTE_EXPIRED") { - // Create new quote and execute - const newQuote = await createQuote(originalQuoteParams); - return await executeQuote(newQuote.id); - } - throw error; - } -} -``` - - - - -**Cause:** Internal account doesn't have enough funds - -**Solution:** Check balance before initiating transfer - -```javascript -async function safeSendPayment(accountId, amount) { - const account = await getInternalAccount(accountId); - - if (account.balance.amount < amount) { - throw new Error( - `Insufficient balance. Available: ${account.balance.amount}, Required: ${amount}` - ); - } - - return await createTransferOut({ accountId, amount }); -} -``` - - - - -**Cause:** Bank account details are invalid or incomplete - -**Solution:** Validate account details before submission - -```javascript -function validateUSAccount(account) { - if (!account.accountNumber || !account.routingNumber) { - throw new Error("Account and routing numbers required"); - } - - if (account.routingNumber.length !== 9) { - throw new Error("Routing number must be 9 digits"); - } - - return true; -} -``` - - - - -## Transaction failure reasons - -When a transaction fails, the `failureReason` field provides specific details: - -### Outgoing payment failures - -```json -{ - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "FAILED", - "type": "OUTGOING", - "failureReason": "QUOTE_EXECUTION_FAILED" -} -``` - -**Common outgoing failure reasons:** - -- `QUOTE_EXPIRED` - Quote expired before execution -- `QUOTE_EXECUTION_FAILED` - Error executing the quote -- `FUNDING_AMOUNT_MISMATCH` - Funding amount doesn't match expected amount -- `TIMEOUT` - Transaction timed out - -### Incoming payment failures - -```json -{ - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000005", - "status": "FAILED", - "type": "INCOMING", - "failureReason": "PAYMENT_APPROVAL_TIMED_OUT" -} -``` - -**Common incoming failure reasons:** - -- `PAYMENT_APPROVAL_TIMED_OUT` - Webhook approval not received within 5 seconds -- `PAYMENT_APPROVAL_WEBHOOK_ERROR` - Webhook returned an error -- `OFFRAMP_FAILED` - Failed to convert and send funds to destination -- `QUOTE_EXPIRED` - Quote expired during processing - -## Handling failures - -### Monitor transaction status - -```javascript -async function monitorTransaction(transactionId) { - const maxAttempts = 30; // 5 minutes with 10-second intervals - let attempts = 0; - - while (attempts < maxAttempts) { - const transaction = await getTransaction(transactionId); - - if (transaction.status === "COMPLETED") { - return { success: true, transaction }; - } - - if (transaction.status === "FAILED") { - return { - success: false, - transaction, - failureReason: transaction.failureReason, - }; - } - - // Still processing - await new Promise((resolve) => setTimeout(resolve, 10000)); - attempts++; - } - - throw new Error("Transaction monitoring timed out"); -} -``` - -### Retry logic for transient errors - -```javascript -async function createQuoteWithRetry(params, maxRetries = 3) { - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - return await createQuote(params); - } catch (error) { - const isRetryable = - error.status === 500 || - error.status === 424 || - error.code === "QUOTE_REQUEST_FAILED"; - - if (!isRetryable || attempt === maxRetries) { - throw error; - } - - // Exponential backoff - const delay = Math.pow(2, attempt) * 1000; - await new Promise((resolve) => setTimeout(resolve, delay)); - } - } -} -``` - -### Handle webhook failures - -```javascript -app.post("/webhooks/grid", async (req, res) => { - try { - await processWebhook(req.body); - res.status(200).json({ received: true }); - } catch (error) { - console.error("Webhook processing error:", error); - - // For pending payments, default to async processing - if (req.body.transaction?.status === "PENDING") { - // Queue for retry - await queueWebhookForRetry(req.body); - return res.status(202).json({ message: "Queued for processing" }); - } - - // For other webhooks, acknowledge receipt - res.status(200).json({ received: true }); - } -}); -``` - -## Error recovery strategies - - - -Automatically create a new quote when one expires: - -```javascript -async function executeQuoteSafe(quoteId, originalParams) { - try { - return await fetch( - `https://api.lightspark.com/grid/2025-10-13/quotes/${quoteId}/execute`, - { - method: "POST", - headers: { Authorization: `Basic ${credentials}` }, - } - ); - } catch (error) { - if (error.status === 409 && error.code === "QUOTE_EXPIRED") { - // Create new quote with same parameters - const newQuote = await createQuote(originalParams); - - // Execute immediately - return await fetch( - `https://api.lightspark.com/grid/2025-10-13/quotes/${newQuote.id}/execute`, - { - method: "POST", - headers: { Authorization: `Basic ${credentials}` }, - } - ); - } - - throw error; - } -} -``` - - - - -Notify users and suggest funding: - -```javascript -async function handleInsufficientBalance(customerId, requiredAmount) { - const accounts = await getInternalAccounts(customerId); - const account = accounts.data[0]; - - const shortfall = requiredAmount - account.balance.amount; - - // Notify customer - await sendNotification(customerId, { - type: "INSUFFICIENT_BALANCE", - message: `You need ${formatAmount( - shortfall - )} more to complete this payment`, - action: { - label: "Add Funds", - url: "/deposit", - }, - }); - - // Return funding instructions - return { - error: "INSUFFICIENT_BALANCE", - currentBalance: account.balance.amount, - requiredAmount, - shortfall, - fundingInstructions: account.fundingPaymentInstructions, - }; -} -``` - - - - -Implement retry with exponential backoff: - -```javascript -async function fetchWithRetry(url, options, maxRetries = 3) { - for (let i = 0; i < maxRetries; i++) { - try { - const response = await fetch(url, options); - - if (!response.ok) { - const error = await response.json(); - throw error; - } - - return await response.json(); - } catch (error) { - const isLastAttempt = i === maxRetries - 1; - const isNetworkError = - error.code === "ECONNRESET" || - error.code === "ETIMEDOUT" || - error.status === 500; - - if (isLastAttempt || !isNetworkError) { - throw error; - } - - // Wait before retry (exponential backoff) - const delay = Math.pow(2, i) * 1000; - await new Promise((resolve) => setTimeout(resolve, delay)); - } - } -} -``` - - - - -## User-friendly error messages - -Convert technical errors to user-friendly messages: - -```javascript -function getUserFriendlyMessage(error) { - const errorMessages = { - QUOTE_EXPIRED: "Exchange rate expired. Please try again.", - INSUFFICIENT_BALANCE: "You don't have enough funds for this payment.", - INVALID_BANK_ACCOUNT: - "Bank account details are invalid. Please check and try again.", - PAYMENT_APPROVAL_TIMED_OUT: "Payment approval timed out. Please try again.", - AMOUNT_OUT_OF_RANGE: "Payment amount is too high or too low.", - INVALID_CURRENCY: "This currency is not supported.", - WEBHOOK_ENDPOINT_NOT_SET: - "Payment receiving is not configured. Contact support.", - }; - - return ( - errorMessages[error.code] || - error.message || - "An unexpected error occurred. Please try again or contact support." - ); -} - -// Usage -try { - await createQuote(params); -} catch (error) { - const userMessage = getUserFriendlyMessage(error); - showErrorToUser(userMessage); -} -``` - -## Logging and monitoring - -Implement comprehensive error logging: - -```javascript -class PaymentErrorLogger { - static async logError(error, context) { - const errorLog = { - timestamp: new Date().toISOString(), - errorCode: error.code, - errorMessage: error.message, - httpStatus: error.status, - context: { - customerId: context.customerId, - transactionId: context.transactionId, - operation: context.operation, - }, - stackTrace: error.stack, - }; - - // Log to your monitoring service - await logToMonitoring(errorLog); - - // Alert on critical errors - if (error.status >= 500 || error.code === "WEBHOOK_DELIVERY_ERROR") { - await sendAlert({ - severity: "high", - message: `Payment error: ${error.code}`, - details: errorLog, - }); - } - - return errorLog; - } -} - -// Usage -try { - await executeQuote(quoteId); -} catch (error) { - await PaymentErrorLogger.logError(error, { - customerId: "Customer:123", - operation: "executeQuote", - }); - throw error; -} -``` - -## Best practices - - - -Validate data on your side before making API requests to catch errors early: - -```javascript -function validateTransferRequest(request) { - const errors = []; - - if (!request.source?.accountId) { - errors.push("Source account ID is required"); - } - - if (!request.destination?.accountId) { - errors.push("Destination account ID is required"); - } - - if (!request.amount || request.amount <= 0) { - errors.push("Amount must be greater than 0"); - } - - if (errors.length > 0) { - throw new ValidationError(errors.join(", ")); - } - - return true; -} -``` - - - - -Store transaction IDs to prevent duplicate submissions on retry: - -```javascript -const processedTransactions = new Set(); - -async function createTransferIdempotent(params) { - const idempotencyKey = generateKey(params); - - if (processedTransactions.has(idempotencyKey)) { - throw new Error("Transaction already processed"); - } - - try { - const result = await createTransferOut(params); - processedTransactions.add(idempotencyKey); - return result; - } catch (error) { - // Don't mark as processed on error - throw error; - } -} -``` - - - - -Configure timeouts for long-running operations: - -```javascript -async function executeWithTimeout(promise, timeoutMs = 30000) { - const timeout = new Promise((_, reject) => - setTimeout(() => reject(new Error("Operation timed out")), timeoutMs) - ); - - return Promise.race([promise, timeout]); -} - -// Usage -try { - const result = await executeWithTimeout( - executeQuote(quoteId), - 30000 // 30 seconds - ); -} catch (error) { - if (error.message === "Operation timed out") { - // Handle timeout specifically - console.log("Quote execution timed out, checking status..."); - const transaction = await checkTransactionStatus(quoteId); - } -} -``` - - - - -## Next steps - - - - Learn how to send payments from internal accounts - - - - Query and filter payment history - - - - Match payments with your internal systems - - - - diff --git a/mintlify/snippets/external-accounts.mdx b/mintlify/snippets/external-accounts.mdx deleted file mode 100644 index 810b8d36..00000000 --- a/mintlify/snippets/external-accounts.mdx +++ /dev/null @@ -1,354 +0,0 @@ -External accounts are bank accounts, cryptocurrency wallets, or payment destinations outside Grid where you can send funds. Grid supports two types: - -- **Customer external accounts** - Scoped to individual customers, used for withdrawals and customer-specific payouts -- **Platform external accounts** - Scoped to your platform, used for platform-wide operations like receiving funds from external sources - - - Customer external accounts often require some basic beneficiary information for compliance. - Platform accounts are managed at the organization level. - - -## Create external accounts by region or wallet - - - -**ACH, Wire, RTP** - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "currency": "USD", - "platformAccountId": "user_123_primary_bank", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "bankName": "Chase Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "John Doe", - "birthDate": "1990-01-15", - "nationality": "US", - "address": { - "line1": "123 Main Street", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } - }' -``` - - - Category must be `CHECKING` or `SAVINGS`. Routing number must be 9 digits. - - - - -**CLABE/SPEI** - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "currency": "MXN", - "platformAccountId": "mx_beneficiary_001", - "accountInfo": { - "accountType": "CLABE", - "clabeNumber": "123456789012345678", - "bankName": "BBVA Mexico", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "María García", - "birthDate": "1985-03-15", - "nationality": "MX", - "address": { - "line1": "Av. Reforma 123", - "city": "Ciudad de México", - "state": "CDMX", - "postalCode": "06600", - "country": "MX" - } - } - } - }' -``` - - - - -**PIX** - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "currency": "BRL", - "platformAccountId": "br_pix_001", - "accountInfo": { - "accountType": "PIX", - "pixKey": "user@email.com", - "pixKeyType": "EMAIL", - "bankName": "Nubank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "João Silva", - "birthDate": "1988-07-22", - "nationality": "BR", - "address": { - "line1": "Rua das Flores 456", - "city": "São Paulo", - "state": "SP", - "postalCode": "01234-567", - "country": "BR" - } - } - } - }' -``` - -Key types: `CPF`, `CNPJ`, `EMAIL`, `PHONE`, or `RANDOM` - - - -**IBAN/SEPA** - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "currency": "EUR", - "platformAccountId": "eu_iban_001", - "accountInfo": { - "accountType": "IBAN", - "iban": "DE89370400440532013000", - "swiftBic": "DEUTDEFF", - "bankName": "Deutsche Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Hans Schmidt", - "birthDate": "1982-11-08", - "nationality": "DE", - "address": { - "line1": "Hauptstraße 789", - "city": "Berlin", - "state": "Berlin", - "postalCode": "10115", - "country": "DE" - } - } - } - }' -``` - - - - -**UPI** - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "currency": "INR", - "platformAccountId": "in_upi_001", - "accountInfo": { - "accountType": "UPI", - "vpa": "user@okbank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "Priya Sharma", - "birthDate": "1991-05-14", - "nationality": "IN", - "address": { - "line1": "123 MG Road", - "city": "Mumbai", - "state": "Maharashtra", - "postalCode": "400001", - "country": "IN" - } - } - } - }' -``` - - - - -**Bitcoin Lightning (Spark Wallet)** - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "currency": "BTC", - "platformAccountId": "btc_spark_001", - "accountInfo": { - "accountType": "SPARK_WALLET", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - }' -``` - - - Spark wallets don't require beneficiary information as they are self-custody wallets. - - - -Use `platformAccountId` to tie your internal id with the external account. - -**Sample Response:** - -```json -{ - "id": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "status": "ACTIVE", - "currency": "USD", - "platformAccountId": "user_123_primary_bank", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "bankName": "Chase Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "John Doe", - "birthDate": "1990-01-15", - "nationality": "US", - "address": { - "line1": "123 Main Street", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } -} -``` - -### Business beneficiaries - -For business accounts, include business information: - -```json -{ - "currency": "USD", - "platformAccountId": "acme_corp_account", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "987654321", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "bankName": "Chase Bank", - "beneficiary": { - "beneficiaryType": "BUSINESS", - "businessInfo": { - "legalName": "Acme Corporation, Inc.", - "taxId": "EIN-987654321" - }, - "address": { - "line1": "456 Business Ave", - "city": "New York", - "state": "NY", - "postalCode": "10001", - "country": "US" - } - } - } -} -``` - -## Account status -Beneficiary data may be reviewed for risk and compliance. Only `ACTIVE` accounts can receive payments. Updates to account data may trigger account re-review. - -| Status | Description | -| -------------- | ----------------------------------- | -| `PENDING` | Created, awaiting verification | -| `ACTIVE` | Verified and ready for transactions | -| `UNDER_REVIEW` | Additional review required | -| `INACTIVE` | Disabled, cannot be used | - -## Listing external accounts - -### List customer accounts - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -### List platform accounts - -For platform-wide operations, list all platform-level external accounts: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/platform/external-accounts' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - - - Platform external accounts are used for platform-wide operations like - depositing funds from external sources. - - -## Best practices - - - -Validate account details before submission: - -```javascript -// US accounts: 9-digit routing, 4-17 digit account number -if (!/^\d{9}$/.test(routingNumber)) { - throw new Error("Invalid routing number"); -} - -// CLABE: exactly 18 digits -if (!/^\d{18}$/.test(clabeNumber)) { - throw new Error("Invalid CLABE number"); -} -``` - - - - -Verify status before sending payments: - -```javascript -if (account.status !== "ACTIVE") { - throw new Error(`Account is ${account.status}, cannot process payment`); -} -``` - - - - -Never expose full account numbers. Display only masked info: - -```javascript -function displaySafely(account) { - return { - id: account.id, - bankName: account.accountInfo.bankName, - lastFour: account.accountInfo.accountNumber.slice(-4), - status: account.status, - }; -} -``` - - - diff --git a/mintlify/snippets/internal-accounts.mdx b/mintlify/snippets/internal-accounts.mdx deleted file mode 100644 index 496d7ef2..00000000 --- a/mintlify/snippets/internal-accounts.mdx +++ /dev/null @@ -1,355 +0,0 @@ -Internal accounts are Lightspark managed accounts that hold funds within the Grid platform. They allow you to receive deposits and send payments to external bank accounts or other payment destinations. - -They are useful for holding funds on behalf or the platform or customers which will be used for instant, 24/7 quotes and transfers out of the system. - -Internal accounts are created for both: - -- **Platform-level accounts**: Hold pooled funds for your platform operations (rewards distribution, reconciliation, etc.) -- **Customer accounts**: Hold individual customer funds for their transactions - - - Internal accounts are automatically created when you onboard a customer, based - on your platform's currency configuration. Platform-level internal accounts - are created when you configure your platform with supported currencies. - - -## How internal accounts work - -Internal accounts act as an intermediary holding account in the payment flow: - -1. **Deposit funds**: You or your customers deposit money into internal accounts using bank transfers (ACH, wire, PIX, etc.) or crypto transfers -2. **Hold balance**: Funds are held securely in the internal account until needed -3. **Send payments**: You initiate transfers from internal accounts to external destinations - -Each internal account: - -- Is denominated in a single currency (USD, EUR, etc.) -- Has a unique balance that you can query at any time -- Includes unique payment instructions for depositing funds -- Supports multiple funding methods depending on the currency - -## Retrieving internal accounts - -### List customer internal accounts - -To retrieve all internal accounts for a specific customer, use the customer ID to filter the results: - - -```bash Request internal accounts for a customer -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - -```json -{ - "data": [ - { - "id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "balance": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - }, - "fundingPaymentInstructions": [ - { - "instructionsNotes": "Include the reference code in your ACH transfer memo", - "accountOrWalletInfo": { - "reference": "FUND-ABC123", - "accountType": "US_ACCOUNT", - "accountNumber": "9876543210", - "routingNumber": "021000021", - "accountHolderName": "Lightspark Payments FBO John Doe", - "bankName": "JP Morgan Chase" - } - }, - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ], - "createdAt": "2025-10-03T12:00:00Z", - "updatedAt": "2025-10-03T14:30:00Z" - } - ], - "hasMore": false, - "totalCount": 1 -} -``` - - -### Filter by currency - -You can filter internal accounts by currency to find accounts for specific denominations: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001¤cy=USD' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -### List platform internal accounts - -To retrieve platform-level internal accounts (not tied to individual customers), use the platform internal accounts endpoint: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/platform/internal-accounts' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - - Platform internal accounts are useful for managing pooled funds, distributing - rewards, or handling platform-level operations. - - -## Understanding funding payment instructions - -Each internal account includes `fundingPaymentInstructions` that tell your customers how to deposit funds. The structure varies by payment rail and currency: - - - - For USD accounts, instructions include routing and account numbers: - - ```json - { - "instructionsNotes": "Include the reference code in your ACH transfer memo", - "accountOrWalletInfo": { - "accountType": "US_ACCOUNT", - "reference": "FUND-ABC123", - "accountNumber": "9876543210", - "routingNumber": "021000021", - "accountHolderName": "Lightspark Payments FBO John Doe", - "bankName": "JP Morgan Chase" - } - } - ``` - - - Each internal account has unique banking details in the `accountOrWalletInfo` - field, which ensures deposits are automatically credited to the correct - account. - - - - - For EUR accounts, instructions use SEPA IBAN numbers: - - ```json - { - "instructionsNotes": "Include reference in SEPA transfer description", - "accountOrWalletInfo": { - "accountType": "IBAN", - "reference": "FUND-EUR789", - "iban": "DE89370400440532013000", - "swiftBic": "DEUTDEFF", - "accountHolderName": "Lightspark Payments FBO Maria Garcia", - "bankName": "Banco de México" - } - } - ``` - - - - For stablecoin accounts, using a Spark wallet as the funding source: - - ```json - { - "invoice": "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs", - "instructionsNotes": "Use the invoice when making Spark payment", - "accountOrWalletInfo": { - "accountType": "SPARK_WALLET", - "assetType": "USDB", - "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - } - } - ``` - - - - For Solana wallet accounts, using a Solana wallet as the funding source: - - ```json - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - } - ``` - - - - For Tron wallet accounts, using a Tron wallet as the funding source: - - ```json - { - "accountOrWalletInfo": { - "accountType": "TRON_WALLET", - "assetType": "USDT", - "address": "TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL" - } - } - ``` - - - - For Polygon wallet accounts, using a Polygon wallet as the funding source: - - ```json - { - "accountOrWalletInfo": { - "accountType": "POLYGON_WALLET", - "assetType": "USDC", - "address": "0xAbCDEF1234567890aBCdEf1234567890ABcDef12" - } - } - ``` - - - - For Base wallet accounts, using a Base wallet as the funding source: - - ```json - { - "accountOrWalletInfo": { - "accountType": "BASE_WALLET", - "assetType": "USDC", - "address": "0xAbCDEF1234567890aBCdEf1234567890ABcDef12" - } - } - ``` - - - -## Checking account balances - -The internal account balance reflects all deposits and withdrawals. The balance includes: - -- **amount**: The balance amount in the smallest currency unit (cents for USD, centavos for MXN/BRL, etc.) -- **currency**: Full currency details including code, name, symbol, and decimal places - -### Example balance check - -```bash Fetch the balance of an internal account -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts/InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - -```json -{ - "data": { - "id": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "balance": { - "amount": 50000, - "currency": { - "code": "USD", - "name": "United States Dollar", - "symbol": "$", - "decimals": 2 - } - } - } -} -``` - - - - Always check the `decimals` field in the currency object to correctly convert - between display amounts and API amounts. For example, USD has 2 decimals, so - an amount of 50000 represents $500.00. - - -## Displaying funding instructions to customers - -When customers need to deposit funds themselves, display the funding payment instructions in your application: - - - -Fetch the customer's internal account for their desired currency using the API. - - - -Parse the `fundingPaymentInstructions` array and select the appropriate instructions based on your customer's preferred payment method. - -```javascript -const instructions = account.fundingPaymentInstructions[0]; -const bankInfo = instructions.accountOrWalletInfo; -``` - - - - -Show the payment details prominently in your UI: - -- Account holder name -- Bank name and routing information (account/routing number, CLABE, PIX key, etc.) -- Reference code (if provided) -- Any additional notes from `instructionsNotes` - - - The unique banking details in each internal account automatically route - deposits to the correct destination. - - - - - -Set up webhook listeners to receive notifications when deposits are credited to the internal account. The account balance will update automatically. - - -You'll receive `ACCOUNT_STATUS` webhook events when the internal account balance changes. - - - - -## Best practices - - - -Ensure your customers have all the information needed to make deposits. Consider implementing: -- Clear display of all banking details from `fundingPaymentInstructions` -- Copy-to-clipboard functionality for account numbers and reference codes -- Email/SMS confirmations with complete deposit instructions - - - -Set up monitoring to alert customers when their balance is low: -```javascript -if (account.balance.amount < minimumThreshold) { - await notifyCustomer({ - type: 'LOW_BALANCE', - account: account.id, - instructions: account.fundingPaymentInstructions - }); -} -``` - - - -If your platform supports multiple currencies, organize internal accounts by currency in your UI: -```javascript -const accountsByCurrency = accounts.data.reduce((acc, account) => { - const code = account.balance.currency.code; - acc[code] = account; - return acc; -}, {}); - -// Quick lookup: accountsByCurrency['USD'] - -``` - - - -Internal account details (especially funding instructions) rarely change, so you can cache them safely. However, always fetch fresh balance data before initiating transfers. - - diff --git a/mintlify/snippets/kyc/kyc-regulated.mdx b/mintlify/snippets/kyc/kyc-regulated.mdx deleted file mode 100644 index c3f4f27d..00000000 --- a/mintlify/snippets/kyc/kyc-regulated.mdx +++ /dev/null @@ -1,83 +0,0 @@ - -**Regulated platforms** have lighter KYC requirements since they handle compliance verification internally. - - -The KYC/KYB flow allows you to onboard customers through direct API calls. - -Regulated financial institutions can: - -- **Direct API Onboarding**: Create customers directly via API calls with minimal verification -- **Internal KYC/KYB**: Handle identity verification through your own compliance systems -- **Reduced Documentation**: Only provide essential customer information required by your payment counterparty or service provider. -- **Faster Onboarding**: Streamlined process for known, verified customers - -#### Creating Customers via Direct API - -For regulated platforms, you can create customers directly through the API without requiring external KYC verification: - -To register a new customer in the system, use the `POST /customers` endpoint: - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/customers" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "platformCustomerId": "customer_12345", - "customerType": "INDIVIDUAL", - "fullName": "Jane Doe", - "birthDate": "1992-03-25", - "nationality": "US", - "address": { - "line1": "123 Pine Street", - "city": "Seattle", - "state": "WA", - "postalCode": "98101", - "country": "US" - } - }' -``` - -The examples below show a more comprehensive set of data. Not all fields are strictly required by the API for customer creation itself, but become necessary based on currency and UMA provider requirements if using UMA. - - - - ```json - { - "platformCustomerId": "9f84e0c2a72c4fa", - "customerType": "INDIVIDUAL", - "fullName": "John Sender", - "birthDate": "1985-06-15", - "address": { - "line1": "Paseo de la Reforma 222", - "line2": "Piso 15", - "city": "Ciudad de México", - "state": "Ciudad de México", - "postalCode": "06600", - "country": "MX" - } - } - ``` - - - - ```json - { - "platformCustomerId": "b87d2e4a9c13f5b", - "customerType": "BUSINESS", - "businessInfo": { - "legalName": "Acme Corporation", - "registrationNumber": "789012345", - "taxId": "123-45-6789" - }, - "address": { - "line1": "456 Oak Avenue", - "line2": "Floor 12", - "city": "New York", - "state": "NY", - "postalCode": "10001", - "country": "US" - } - } - ``` - - diff --git a/mintlify/snippets/kyc/kyc-unregulated.mdx b/mintlify/snippets/kyc/kyc-unregulated.mdx deleted file mode 100644 index 1dfd3f5b..00000000 --- a/mintlify/snippets/kyc/kyc-unregulated.mdx +++ /dev/null @@ -1,73 +0,0 @@ - -**Unregulated platforms** require full KYC/KYB verification of customers through hosted flows. - - -Unregulated platforms must: - -- **Hosted KYC Flow**: Use the hosted KYC link for complete identity verification -- **Extended Review**: Customers may require manual review and approval in some cases - -### Hosted KYC Link Flow - -The hosted KYC flow provides a secure, hosted interface where customers can complete their identity verification and onboarding process. - -#### Generate KYC Link - - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/customers/kyc-link?redirectUri=https://yourapp.com/onboarding-complete&platformCustomerId=019542f5-b3e7-1d02-0000-000000000001" \ - -H "Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -**Response:** - -```json -{ - "kycUrl": "https://kyc.lightspark.com/onboard/abc123def456", - "platformCustomerId": "019542f5-b3e7-1d02-0000-000000000001" -} -``` - -#### Complete KYC Process - - - - Call the `/customers/kyc-link` endpoint with your `redirectUri` parameter to generate a hosted KYC URL for your customer. - - - The `redirectUri` parameter is embedded in the generated KYC URL and will be used to automatically redirect the customer back to your application after they complete verification. - - - - - Redirect your customer to the returned `kycUrl` where they can complete their identity verification in the hosted interface. - - - The KYC link is single-use and expires after a limited time period for security. - - - - - The customer completes the identity verification process in the hosted KYC interface, providing required documents and information. - - - The hosted interface handles document collection, verification checks, and compliance requirements automatically. - - - - After verification processing, you'll receive a KYC status webhook notification indicating the final verification result. - - - - - Upon successful KYC completion, the customer is automatically redirected to your specified `redirectUri` URL. - - - The customer account will be automatically created by the system upon successful KYC completion. You can identify the new customer using your `platformCustomerId` or other identifiers. - - - - - On your redirect page, handle the completed KYC flow and integrate the new customer into your application. - - \ No newline at end of file diff --git a/mintlify/snippets/kyc/kyc-webhooks.mdx b/mintlify/snippets/kyc/kyc-webhooks.mdx deleted file mode 100644 index e380222c..00000000 --- a/mintlify/snippets/kyc/kyc-webhooks.mdx +++ /dev/null @@ -1,92 +0,0 @@ -After a customer completes the KYC/KYB verification process, you'll receive webhook notifications about their KYC status. These notifications are sent to your configured webhook endpoint. - - -For regulated platforms, customers are created with `APPROVED` KYC status by default. - - -**Webhook Payload (sent to your endpoint):** - -```json -{ - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000020", - "type": "KYC_STATUS", - "timestamp": "2023-07-21T17:32:28Z", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "kycStatus": "APPROVED", - "platformCustomerId": "1234567" -} -``` - -**Webhook Headers:** -- `Content-Type: application/json` -- `X-Webhook-Signature: sha256=abc123...` - - -System-generated unique identifier of the customer whose KYC status has changed. - - - -Final KYC verification status. Webhooks are only sent for final states: -- `APPROVED`: Customer verification completed successfully -- `REJECTED`: Customer verification was rejected -- `EXPIRED`: KYC verification has expired and needs renewal -- `CANCELED`: Verification process was canceled -- `MANUALLY_APPROVED`: Customer was manually approved by platform -- `MANUALLY_REJECTED`: Customer was manually rejected by platform - - - -Intermediate states like `PENDING_REVIEW` do not trigger webhook notifications. Only final resolution states will send webhook notifications. - - - -```javascript -// Example webhook handler for KYC status updates -// Note: Only final states trigger webhook notifications -app.post('/webhooks/kyc-status', (req, res) => { - const { customerId, kycStatus } = req.body; - - switch (kycStatus) { - case 'APPROVED': - // Activate customer account - await activateCustomer(customerId); - await sendWelcomeEmail(customerId); - break; - - case 'REJECTED': - // Notify support and customer - await notifySupport(customerId, 'KYC_REJECTED'); - await sendRejectionEmail(customerId); - break; - - case 'MANUALLY_APPROVED': - // Handle manual approval - await activateCustomer(customerId); - await sendWelcomeEmail(customerId); - break; - - case 'MANUALLY_REJECTED': - // Handle manual rejection - await notifySupport(customerId, 'KYC_MANUALLY_REJECTED'); - await sendRejectionEmail(customerId); - break; - - case 'EXPIRED': - // Handle expired KYC - await notifyCustomerForReKyc(customerId); - break; - - case 'CANCELED': - // Handle canceled verification - await logKycCancelation(customerId); - break; - - default: - // Log unexpected statuses - console.log(`Unexpected KYC status ${kycStatus} for customer ${customerId}`); - } - - res.status(200).send('OK'); -}); -``` - \ No newline at end of file diff --git a/mintlify/snippets/payment-flow.mdx b/mintlify/snippets/payment-flow.mdx deleted file mode 100644 index e9580a6b..00000000 --- a/mintlify/snippets/payment-flow.mdx +++ /dev/null @@ -1,12 +0,0 @@ -## Payment Flow - - - You can either prefund an internal account with fiat or receive just-in-time payment instructions as part of the quote. - - - Create a quote to lock exchange rate to the receiving foreign account and parse payment instructions. - - - Execute the quote or send funds as per payment instructions to initiate the transfer from the internal account to the external bank account. - - diff --git a/mintlify/snippets/plaid-integration.mdx b/mintlify/snippets/plaid-integration.mdx deleted file mode 100644 index 95800db1..00000000 --- a/mintlify/snippets/plaid-integration.mdx +++ /dev/null @@ -1,318 +0,0 @@ -Plaid integration allows your customers to securely connect their bank accounts without manually entering account numbers and routing information. Grid handles the complete Plaid Link flow, automatically creating external accounts when customers authenticate their banks. - - - Plaid integration requires Grid to manage your Plaid configuration. Contact - support to enable Plaid for your platform. - - -## Overview - -The Plaid flow involves collaboration between your platform, Grid, Plaid, and the customer's bank: - -1. **Request link token**: Your platform requests a Plaid Link token from Grid for a specific customer -2. **Initialize Plaid Link**: Display Plaid Link UI to your customer using the link token -3. **Customer authenticates**: Customer selects their bank and authenticates using Plaid Link -4. **Exchange tokens**: Plaid returns a public token; your platform sends it to Grid's callback URL -5. **Async processing**: Grid exchanges the public token with Plaid and retrieves account details -6. **External account created**: Grid creates the external account and sends a webhook notification. The external account is available for transfers and payments - -## Request a Plaid Link token - -To initiate the Plaid flow, request a link token from Grid: - - -```bash cURL -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/plaid/link-tokens' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001" - }' -``` - - -**Response:** - -```json -{ - "linkToken": "link-sandbox-af1a0311-da53-4636-b754-dd15cc058176", - "expiration": "2025-10-05T18:30:00Z", - "callbackUrl": "https://api.lightspark.com/grid/2025-10-13/plaid/callback/link-sandbox-af1a0311-da53-4636-b754-dd15cc058176", - "requestId": "req_abc123def456" -} -``` - - -Store the `callbackUrl` when you request the link token so you can retrieve it later when exchanging the public token. - - -### Key response fields: - -- **`linkToken`**: Use this to initialize Plaid Link in your frontend -- **`callbackUrl`**: Where to POST the public token after Plaid authentication completes. The URL follows the pattern `https://api.lightspark.com/grid/{version}/plaid/callback/{linkToken}`. While you can construct this manually, we recommend using the provided URL for forward compatibility. -- **`expiration`**: Link tokens typically expire after 4 hours -- **`requestId`**: Unique identifier for debugging purposes - - - Link tokens are single-use and will expire. If the customer doesn't complete - the flow, you'll need to request a new link token. - - -## Initialize Plaid Link - -Display the Plaid Link UI to your customer using the link token. The implementation varies by platform: - - -Install the appropriate Plaid SDK for your platform: -- React: `npm install react-plaid-link` -- React Native: `npm install react-native-plaid-link-sdk` -- Vanilla JS: Include the Plaid script tag as shown above - - - - -```javascript -import { usePlaidLink } from 'react-plaid-link'; - -function BankAccountConnector({ linkToken, onSuccess }) { -const { open, ready } = usePlaidLink({ -token: linkToken, -onSuccess: async (publicToken, metadata) => { -console.log('Plaid authentication successful'); - - // Send public token to YOUR backend endpoint - await fetch('/api/plaid/exchange-token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - publicToken: publicToken, - accountId: metadata.account_id, // Optional - }), - }); - - onSuccess(); - }, - onExit: (error, metadata) => { - if (error) { - console.error('Plaid Link error:', error); - } - console.log('User exited Plaid Link'); - }, - -}); - -return ( - - -); } - -```` - - - -```javascript -import { PlaidLink } from 'react-native-plaid-link-sdk'; - -function BankAccountConnector({ linkToken, onSuccess }) { - return ( - { - console.log('Plaid authentication successful'); - - // Send public token to YOUR backend endpoint - await fetch('https://yourapi.com/api/plaid/exchange-token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - publicToken: publicToken, - accountId: metadata.account_id, - }), - }); - - onSuccess(); - }} - onExit={(error, metadata) => { - if (error) { - console.error('Plaid Link error:', error); - } - }} - > - Connect your bank account - - ); -} -```` - - - - -```html - - - - - - -```` - - - -## Exchange the public token on your backend - -Create a backend endpoint that receives the public token from your frontend and forwards it to Grid's callback URL: - - -```javascript Express -// Backend endpoint: POST /api/plaid/exchange-token -app.post('/api/plaid/exchange-token', async (req, res) => { - const { publicToken, accountId } = req.body; - const customerId = req.user.gridCustomerId; // From your auth - - try { - // Get the callback URL (you stored this when requesting the link token) - const callbackUrl = await getStoredCallbackUrl(customerId); - - // Forward to Grid's callback URL with proper authentication - const response = await fetch(callbackUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - publicToken: publicToken, - accountId: accountId, - }), - }); - - if (!response.ok) { - throw new Error(`Grid API error: ${response.status}`); - } - - const result = await response.json(); - res.json({ success: true, message: result.message }); - } catch (error) { - console.error('Error exchanging token:', error); - res.status(500).json({ error: 'Failed to process bank account' }); - } -}); -``` - - -**Response from Grid (HTTP 202 Accepted):** - -```json -{ - "message": "External account creation initiated. You will receive a webhook notification when complete.", - "requestId": "req_def456ghi789" -} -``` - - - A `202 Accepted` response indicates Grid has received the token and is - processing it asynchronously. The external account will be created in the - background. - - -## Handle webhook notification - -After Grid creates the external account, you'll receive an `ACCOUNT_STATUS` webhook. - -```json -{ - "type": "ACCOUNT_STATUS", - "timestamp": "2025-01-15T14:32:10Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-0000000000ac", - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "account": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "status": "ACTIVE", - "currency": "USD", - "platformAccountId": "user_123_primary_bank", - "accountInfo": { - "accountType": "US_ACCOUNT", - "accountNumber": "123456789", - "routingNumber": "021000021", - "accountCategory": "CHECKING", - "bankName": "Chase Bank", - "beneficiary": { - "beneficiaryType": "INDIVIDUAL", - "fullName": "John Doe", - "birthDate": "1990-01-15", - "nationality": "US", - "address": { - "line1": "123 Main Street", - "city": "San Francisco", - "state": "CA", - "postalCode": "94105", - "country": "US" - } - } - } - } -} -``` - -## Error handling - -Handle common error scenarios: - -### User exits Plaid Link - -```javascript -const { open } = usePlaidLink({ - token: linkToken, - onExit: (error, metadata) => { - if (error) { - console.error("Plaid error:", error); - // Show user-friendly error message - setError("Unable to connect to your bank. Please try again."); - } else { - // User closed the modal without completing - console.log("User exited without connecting"); - } - }, -}); -``` diff --git a/mintlify/snippets/platform-config-currency-api-webhooks.mdx b/mintlify/snippets/platform-config-currency-api-webhooks.mdx deleted file mode 100644 index be1ee6bc..00000000 --- a/mintlify/snippets/platform-config-currency-api-webhooks.mdx +++ /dev/null @@ -1,57 +0,0 @@ -## Supported currencies -During onboarding, choose the currencies your platform will support. For prefunded models, Grid automatically creates per‑currency accounts for each new customer. You can add or remove supported currencies anytime in the Grid dashboard. - -## API credentials and authentication -Create API credentials in the Grid dashboard. Credentials are scoped to an environment (Sandbox or Production) and cannot be used across environments. - -- Authentication: Use HTTP Basic Auth with your API key and secret in the `Authorization` header. -- Keys: Sandbox keys only work against Sandbox; Production keys only work against Production. - - -Never share or expose your API secret. Rotate credentials periodically and restrict access. - - -### Example: HTTP Basic Auth in cURL - -```bash -# Using cURL's Basic Auth shorthand (-u): -curl -sS -X GET "https://api.lightspark.com/grid/2025-10-13/config" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" - -``` - -## Base API path - -The base API path is consistent across environments; your credentials determine the environment. - -Base URL: `https://api.lightspark.com/grid/2025-10-13` (same for Sandbox and Production; your keys select the environment). - -## Webhooks and signature verification -Configure your webhook endpoint to receive payment lifecycle events. Webhooks use asymmetric (public/private key) signatures; verify each webhook using the Grid public key available in your dashboard. - -- Expose a public HTTPS endpoint (for development, reverse proxies like ngrok can help). You'll also need to set your webhook endpoint in the Grid dashboard. -- When receiving webhooks, verify the `X-Grid-Signature` header against the exact request body using the dashboard-provided public key -- Process events idempotently and respond with 2xx on success - -You can trigger a test delivery from the API to validate your endpoint setup. The public key for verification is shown in the dashboard; rotate and update it when instructed by Lightspark. - -### Test your webhook endpoint -Use the webhook test endpoint to send a synthetic event to your configured endpoint. - -```bash -curl -sS -X POST "https://api.lightspark.com/grid/2025-10-13/webhooks/test" \ - -u "$GRID_CLIENT_ID:$GRID_API_SECRET" -``` - -Example test webhook payload: - -```json -{ - "test": true, - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000001", - "type": "TEST" -} -``` - -For more details about webhooks like retry policy and examples, take a look at our Webhooks documentation. diff --git a/mintlify/snippets/platform-configuration-non-uma.mdx b/mintlify/snippets/platform-configuration-non-uma.mdx deleted file mode 100644 index 1f65db10..00000000 --- a/mintlify/snippets/platform-configuration-non-uma.mdx +++ /dev/null @@ -1,115 +0,0 @@ -When you first sign up for Grid, a platform will be created for you with preconfigured settings. -Use the `/config` endpoint to view and update your platform configuration for the Grid API. - -## Retrieve Current Configuration - -You can retrieve your current platform configuration to see what settings are already in place: - - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' -``` - -```json Success -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "webhookEndpoint": "https://api.example.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"] - } - ], - "createdAt": "2023-06-15T12:30:45Z", - "updatedAt": "2023-07-01T10:00:00Z" -} -``` - - -If this is your first time configuring the platform, some default values may be returned which were set up when you first created your account. - - -## Update Platform Configuration - -To update your platform configuration for payouts payments, use the PATCH endpoint: - - -```bash cURL -curl -X PATCH 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "webhookEndpoint": "https://api.mycompany.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"], - } - ] - }' -``` - -```json Success -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "webhookEndpoint": "https://api.mycompany.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"] - } - ], - "createdAt": "2023-06-15T12:30:45Z", - "updatedAt": "2023-06-15T12:30:45Z" -} -``` - -Note that not all fields are patchable. If you need to update a field that is not patchable, please contact support. - - -The `webhookSecret` is not returned in the response. It can only be retrieved from the Grid dashboard. Store this securely as it's used to verify the authenticity of webhooks (see the Webhook Verification Guide). - - -## Configuration Parameters - -### Webhook Endpoint - -The `webhookEndpoint` parameter specifies the URL where Grid will send webhook notifications about payment events. Your webhook endpoint must: - -- Be publicly accessible over HTTPS -- Respond with appropriate HTTP status codes -- Handle webhook verification (see Webhook Verification Guide) -- Process webhook payloads within a reasonable time (recommended: under 5 seconds) - -### Supported Currencies - -The `supportedCurrencies` array allows you to define settings for each currency your platform will support for payouts payments. Each object in this array can contain: - -- `currencyCode`: (String, required) The ISO 4217 currency code (e.g., "USD"). -- `minAmount`: (Integer, required) Minimum transaction amount in the smallest unit of the currency. -- `maxAmount`: (Integer, required) Maximum transaction amount in the smallest unit of the currency. -- `enabledTransactionTypes`: (Array, optional) Specifies which transaction types are allowed (e.g., `["OUTGOING", "INCOMING"]`). - - -For payouts payments, you do not need UMA-specific fields like `providerRequiredCustomerFields` or `providerRequiredCounterpartyCustomerFields`. The configuration is simpler and focuses on basic currency requirements. - - -## Verify Configuration - -After updating your configuration, it's recommended to verify that the changes were saved correctly: - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' -``` - - -PATCH may return 400 for validation errors, 401 for authentication issues, and 500/501 for server or unsupported operations. - diff --git a/mintlify/snippets/platform-configuration-uma.mdx b/mintlify/snippets/platform-configuration-uma.mdx deleted file mode 100644 index b903a4cb..00000000 --- a/mintlify/snippets/platform-configuration-uma.mdx +++ /dev/null @@ -1,195 +0,0 @@ - -Use the `/config` endpoint to view and update your platform configuration for UMA address payments. -Authentication: HTTP Basic (client_id as username, client_secret as password). - - -## Retrieve Current Configuration - -You can retrieve your current platform configuration to see what settings are already in place: - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' -``` - -```json Success -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "umaDomain": "example.com", - "proxyUmaSubdomain": "example", - "webhookEndpoint": "https://api.example.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "requiredCounterpartyFields": [ - { "name": "FULL_NAME", "mandatory": true }, - { "name": "BIRTH_DATE", "mandatory": true } - ], - "enabledTransactionTypes": ["OUTGOING", "INCOMING"], - "providerRequiredCustomerFields": [ - "NATIONALITY", - "BIRTH_DATE" - ], - "providerRequiredCounterpartyCustomerFields": [ - "FULL_NAME", - "COUNTRY_OF_RESIDENCE" - ] - } - ], - "createdAt": "2023-06-15T12:30:45Z", - "updatedAt": "2023-07-01T10:00:00Z" -} -``` - - -If this is your first time configuring the platform, some default values may be returned which were set up when you first created your account. - - -## Update Platform Configuration - -To update your platform configuration for UMA payments, use the PATCH endpoint: - -```bash cURL -curl -X PATCH 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \ - -H 'Content-Type: application/json' \ - -d '{ - "umaDomain": "mycompany.com", - "webhookEndpoint": "https://api.mycompany.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "enabledTransactionTypes": ["OUTGOING", "INCOMING"], - "requiredCounterpartyFields": [ - { "name": "FULL_NAME", "mandatory": true }, - { "name": "BIRTH_DATE", "mandatory": true }, - { "name": "ADDRESS", "mandatory": false } - ] - } - ] - }' -``` - -```json Success -{ - "id": "PlatformConfig:019542f5-b3e7-1d02-0000-000000000003", - "umaDomain": "mycompany.com", - "proxyUmaSubdomain": "mycompany", - "webhookEndpoint": "https://api.mycompany.com/webhooks/grid", - "supportedCurrencies": [ - { - "currencyCode": "USD", - "minAmount": 100, - "maxAmount": 1000000, - "requiredCounterpartyFields": [ - { "name": "FULL_NAME", "mandatory": true }, - { "name": "BIRTH_DATE", "mandatory": true }, - { "name": "ADDRESS", "mandatory": false } - ], - "enabledTransactionTypes": ["OUTGOING", "INCOMING"], - "providerRequiredCustomerFields": [ - "NATIONALITY", - "BIRTH_DATE" - ], - "providerRequiredCounterpartyCustomerFields": [ - "FULL_NAME", - "COUNTRY_OF_RESIDENCE" - ] - } - ], - "createdAt": "2023-06-15T12:30:45Z", - "updatedAt": "2023-06-15T12:30:45Z" -} -``` - - -The `webhookSecret` is not returned in the response. It can only be retrieved from the Grid dashboard. Store this securely as it's used to verify the authenticity of webhooks (see the Webhook Verification Guide). - - -## Configuration Parameters for UMA Payments - -### UMA Domain - -The `umaDomain` parameter defines the domain part of all UMA addresses for your users. For example, if you set `umaDomain` to `mycompany.com`, your users' UMA addresses will follow the format `$username@mycompany.com`. - -Requirements for a valid UMA domain: - -- Must be a valid domain name format -- Should be a domain that you control -- Must proxy incoming requests to the Grid API as follows: - - `https:///.well-known/lnurlp/*` -> `https://.grid.lightspark.com/.well-known/lnurlp/*` - - `https:///.well-known/lnurlpubkey` -> `https://.grid.lightspark.com/.well-known/lnurlpubkey` - - `https:///.well-known/uma-configuration` -> `https://.grid.lightspark.com/.well-known/uma-configuration` - -### Webhook Endpoint - -The `webhookEndpoint` parameter specifies the URL where Grid will send webhook notifications about payment events. Your webhook endpoint must: - -- Be publicly accessible over HTTPS -- Respond with appropriate HTTP status codes -- Handle webhook verification (see Webhook Verification Guide) -- Process webhook payloads within a reasonable time (recommended: under 5 seconds) - -### Supported Currencies for UMA - -The `supportedCurrencies` array allows you to define settings for each currency your platform will support for UMA payments. Each object in this array can contain: - -- `currencyCode`: (String, required) The ISO 4217 currency code (e.g., "USD"). -- `minAmount`: (Integer, required) Minimum transaction amount in the smallest unit of the currency. -- `maxAmount`: (Integer, required) Maximum transaction amount in the smallest unit of the currency. -- `requiredCounterpartyFields`: (Array, required) Defines PII your platform requires about *external counterparties* for transactions in this currency. -- `enabledTransactionTypes`: (Array, optional) Specifies which transaction types are allowed (e.g., `["OUTGOING", "INCOMING"]`). -- `providerRequiredCustomerFields`: (Array, read-only) Lists user info field names that the UMA provider mandates for *your own users* to transact in this currency. -- `providerRequiredCounterpartyCustomerFields`: (Array, read-only) Lists counterparty field names that the UMA provider mandates for external counterparties. - -### Required Counterparty Fields - -Within each currency defined in `supportedCurrencies`, the `requiredCounterpartyFields` parameter allows you to specify what information your platform needs to collect from *external counterparties* (senders or receivers) involved in transactions with your users for that specific currency. - -Available counterparty fields (to be specified with a `name` and `mandatory` flag): - -| Field Name (type `UserInfoFieldName`) | Description | -|---------------------------------------|-------------| -| `FULL_NAME` | Full legal name of the individual or business | -| `BIRTH_DATE` | Date of birth in YYYY-MM-DD format (for individuals) | -| `NATIONALITY` | Nationality of the individual | -| `ADDRESS` | Physical address including country, city, etc. | -| `PHONE_NUMBER` | Contact phone number including country code | -| `EMAIL` | Email address | -| `BUSINESS_NAME` | Legal business name (for business entities) | -| `TAX_ID` | Tax identification number | -| `COUNTRY_OF_RESIDENCE` | Country where the individual resides | - -Each field in `requiredCounterpartyFields` is an object containing: - -- `name`: The `UserInfoFieldName` representing the PII you require. -- `mandatory`: A boolean (true/false) indicating if this field is strictly required by your platform for transactions in this currency. - -This information will be provided to your platform via webhooks for pending payments, allowing you to screen the counterparty based on your compliance rules before approving the payment. - -### UMA Provider Required Fields - -Also within each currency defined in `supportedCurrencies`, you will find two arrays: - -- `providerRequiredCustomerFields`: Lists user information fields mandated by the UMA provider for *your own registered users* to transact in this currency. -- `providerRequiredCounterpartyCustomerFields`: Lists counterparty information fields mandated by the UMA provider for external counterparties. - -These fields must be supplied when creating or updating users or counterparties via the respective API endpoints if they are expected to use the specified currency. -Refer to the [Configuring Customers](/payouts-and-b2b/onboarding/configuring-customers) guide for more details on how this impacts user setup. - -## Verify Configuration - -After updating your configuration, it's recommended to verify that the changes were saved correctly: - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/config' \ - -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' -``` - - -PATCH may return 400 for validation errors, 401 for authentication issues, and 500/501 for server or unsupported operations. - diff --git a/mintlify/snippets/postman-collection.mdx b/mintlify/snippets/postman-collection.mdx deleted file mode 100644 index 649d368e..00000000 --- a/mintlify/snippets/postman-collection.mdx +++ /dev/null @@ -1,9 +0,0 @@ - -Use our hosted Postman collection to explore endpoints and send test requests quickly. - - - -Launch the collection in Postman. - - - diff --git a/mintlify/snippets/reconciliation/reconciliation.mdx b/mintlify/snippets/reconciliation/reconciliation.mdx deleted file mode 100644 index 53b692e5..00000000 --- a/mintlify/snippets/reconciliation/reconciliation.mdx +++ /dev/null @@ -1,130 +0,0 @@ -This guide explains how to reconcile transactions between your internal systems and the Grid API using two complementary mechanisms: real-time webhooks and periodic queries. - - -Use webhooks for real-time updates and daily queries as a backstop to detect missed events or data drift. - - -## Handling webhooks - -Listen for transaction webhooks to track transaction status change until a terminal state is reached. - - -Terminal statuses: `COMPLETED`, `REJECTED`, `FAILED`, `REFUNDED`, `EXPIRED`. -All other statuses will progress until reaching one of the above. - - -- **Outbound transactions**: The originating account is debited at transaction creation. If the transaction ultimately fails, a refund is posted back to the originating account. -- **Inbound transactions**: The receiving account is credited only on success. Failures do not change balances. - - -Grid retries failed webhooks up to 160 times over 7 days with exponential backoff. Use the dashboard to review and remediate webhook delivery issues. - - - - -Configure your webhook endpoint and verify signatures. See Webhooks. - -Sample webhook payload: - -```json -{ - "transaction": { - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "COMPLETED", - "type": "OUTGOING", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "sentAmount": { - "amount": 10000, - "currency": { "code": "USD", "symbol": "$", "decimals": 2 } - }, - "receivedAmount": { - "amount": 9200, - "currency": { "code": "EUR", "symbol": "€", "decimals": 2 } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "customer_12345", - "createdAt": "2025-10-03T15:00:00Z", - "settledAt": "2025-10-03T15:30:00Z", - "description": "Payment for services - Invoice #1234" - }, - "timestamp": "2025-10-03T15:30:01Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-0000000000ab", - "type": "OUTGOING_PAYMENT" -} -``` - - - -Use the `webhookId`, `transaction.id`, and `timestamp` to ensure idempotent handling, updating your internal ledger on each status transition. - - - -When a transaction reaches a terminal state, finalize your reconciliation for that transaction. - - - -## Reconcile via queries - -Additionally, you can list transactions for a time window and compare with your internal records. - - -We recommend querying days from `00:00:00.000` to `23:59:59.999` in your preferred timezone. - - -```bash cURL -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions?startDate=2025-10-01T00:00:00.000Z&endDate=2025-10-01T23:59:59.999Z&limit=100' \ - -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' -``` - -Response -```json -{ - "data": [ - { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "COMPLETED", - "type": "OUTGOING", - "source": { - "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "EUR" - }, - "sentAmount": { - "amount": 10000, - "currency": { "code": "USD", "symbol": "$", "decimals": 2 } - }, - "receivedAmount": { - "amount": 9200, - "currency": { "code": "EUR", "symbol": "€", "decimals": 2 } - }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "customer_12345", - "description": "Payment for services - Invoice #1234", - "exchangeRate": 0.92, - "settledAt": "2025-10-03T15:30:00Z", - "createdAt": "2025-10-03T15:00:00Z" - } - ], - "hasMore": true, - "nextCursor": "eyJpZCI6IlRyYW5zYWN0aW9uOjAxOTU0MmY1LWIzZTctMWQwMi0wMDAwLTAwMDAwMDAwMDAzMCJ9", - "totalCount": 45 -} -``` - -## Troubleshooting - -- **Missing webhook**: Check delivery logs in the dashboard and ensure your endpoint returns `2xx`. Retries continue for 7 days. -- **Mismatched balances**: Re-query the date range and verify terminal statuses; remember outbound failures are refunded, inbound failures do not change balances. -- **Pagination gaps**: Always follow `nextCursor` until `hasMore` is `false`. - - diff --git a/mintlify/snippets/sending/cross-currency.mdx b/mintlify/snippets/sending/cross-currency.mdx deleted file mode 100644 index 469201c9..00000000 --- a/mintlify/snippets/sending/cross-currency.mdx +++ /dev/null @@ -1,232 +0,0 @@ -## Cross-Currency Transfers - -Use the quotes flow when sending funds with currency conversion. This locks in an exchange rate and provides all details needed to execute the transfer. - -### When to use cross-currency transfers - -- Converting USD, USDC, USDT to EUR, MXN, BRL, BTC, or other supported fiat and crypto currencies -- Sending international payments with automatic currency conversion -- Need to lock in a specific exchange rate for the transfer - -### Create and execute a quote - - - - Request a quote to lock in the exchange rate and get transfer details: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" }, - "destination": { "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR" }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Payment for services - Invoice #1234" - }' -``` - -```json Success (201 Created) -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000025", - "status": "PENDING", - "source": { "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", "currency": "USD" }, - "destination": { "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR" }, - "sendingAmount": { "amount": 10000, "currency": { "code": "USD", "decimals": 2 } }, - "receivingAmount": { "amount": 9200, "currency": { "code": "EUR", "decimals": 2 } }, - "exchangeRate": 0.92, - "fee": { "amount": 50, "currency": { "code": "USD", "decimals": 2 } }, - "expiresAt": "2025-10-03T15:15:00Z", - "createdAt": "2025-10-03T15:00:00Z", - "description": "Payment for services - Invoice #1234" -} -``` - - - **Locked currency side** determines which amount is fixed: - - `SENDING`: Lock the sending amount (receiving amount calculated based on exchange rate) - - `RECEIVING`: Lock the receiving amount (sending amount calculated based on exchange rate) - - - - - Before executing, review the quote to ensure: - -- Exchange rate is acceptable -- Fees are as expected -- Receiving amount meets requirements -- Quote hasn't expired (check `expiresAt`) - - - Quotes typically expire after a short period. If expired, create a new quote to get an updated exchange rate. - - - - - Confirm and execute the quote to initiate the transfer: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes/Quote:019542f5-b3e7-1d02-0000-000000000025/execute' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -```json Success (200 OK) -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000025", - "status": "PROCESSING", - "transactionId": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "source": { "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", "currency": "USD" }, - "destination": { "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR" }, - "sendingAmount": { "amount": 10000, "currency": { "code": "USD", "decimals": 2 } }, - "receivingAmount": { "amount": 9200, "currency": { "code": "EUR", "decimals": 2 } }, - "exchangeRate": 0.92, - "executedAt": "2025-10-03T15:05:00Z" -} -``` - - - Once executed, the quote creates a transaction and the transfer begins processing. The `transactionId` can be used to track the payment. - - - - - Track the transfer using webhooks or by polling the transaction: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000030' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -You'll receive a webhook when the transaction completes: - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "COMPLETED", - "sentAmount": { "amount": 10000, "currency": { "code": "USD", "decimals": 2 } }, - "receivedAmount": { "amount": 9200, "currency": { "code": "EUR", "decimals": 2 } }, - "exchangeRate": 0.92, - "settledAt": "2025-10-03T15:30:00Z", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000025" - }, - "timestamp": "2025-10-03T15:31:00Z" -} -``` - - - - -### Funding with cryptocurrencies - -Cross-currency transfers support funding via USDC and BTC on popular blockchains including Solana, Base, Lightning and Spark. When you create a quote specifying the source currency as USDC or BTC, the response includes payment instructions for multiple funding options. - -#### Supported blockchains - -| Blockchain Network | Cryptocurrencies | -|-------|-------------| -| Solana | USDC | -| Base | USDC | -| Tron | USDT | -| Polygon | USDC | -| Lightning | BTC | -| Spark | BTC | - -#### Create a quote for USDC-funded transfer - -Request a quote that provides blockchain funding options: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "currency": "USDC" - }, - "destination": { "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR" }, - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Payment for services - Invoice #1234" - }' -``` - -The response includes an array of payment instructions, including blockchain wallet addresses for USDC and invoices for BTC: - -```json Success (201 Created) -{ - "id": "Quote:019542f5-b3e7-1d02-0000-000000000025", - "status": "PENDING", - "source": { "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", "currency": "USDC" }, - "destination": { "accountId": "ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", "currency": "EUR" }, - "sendingAmount": { "amount": 10000, "currency": { "code": "USDC", "decimals": 2 } }, - "receivingAmount": { "amount": 9200, "currency": { "code": "EUR", "decimals": 2 } }, - "exchangeRate": 0.92, - "fee": { "amount": 50, "currency": { "code": "USD", "decimals": 2 } }, - "expiresAt": "2025-10-03T15:15:00Z", - "createdAt": "2025-10-03T15:00:00Z", - "paymentInstructions": [ - { - "accountOrWalletInfo": { - "accountType": "SOLANA_WALLET", - "assetType": "USDC", - "address": "4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg" - } - }, - { - "accountOrWalletInfo": { - "accountType": "BASE_WALLET", - "assetType": "USDC", - "address": "0x1234567890abcdef1234567890abcdef12345678" - } - } - ] -} -``` - -#### Transaction processing - -Grid automatically detects blockchain deposits and processes the transfer once funds are received: - - - - Transfer the exact amount of USDC specified in `sendingAmount` to your chosen blockchain wallet address. - - - - Grid monitors the blockchain for incoming deposits. You'll receive an `ACCOUNT_STATUS` webhook when the deposit is confirmed: - -```json -{ - "type": "ACCOUNT_STATUS", - "accountId": "InternalAccount:019542f5-b3e7-1d02-0000-000000000025", - "oldBalance": { "amount": 0, "currency": { "code": "USDC", "decimals": 2 } }, - "newBalance": { "amount": 10000, "currency": { "code": "USDC", "decimals": 2 } }, - "timestamp": "2025-10-03T15:05:00Z" -} -``` - - - - Once the deposit is confirmed, Grid executes the cross-currency transfer. You'll receive `OUTGOING_PAYMENT` webhooks as the transfer progresses and completes: - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000030", - "status": "COMPLETED", - "sentAmount": { "amount": 10000, "currency": { "code": "USD", "decimals": 2 } }, - "receivedAmount": { "amount": 9200, "currency": { "code": "EUR", "decimals": 2 } }, - "exchangeRate": 0.92, - "settledAt": "2025-10-03T15:30:00Z", - "quoteId": "Quote:019542f5-b3e7-1d02-0000-000000000025" - }, - "timestamp": "2025-10-03T15:31:00Z" -} -``` - - diff --git a/mintlify/snippets/sending/same-currency.mdx b/mintlify/snippets/sending/same-currency.mdx deleted file mode 100644 index fa229b35..00000000 --- a/mintlify/snippets/sending/same-currency.mdx +++ /dev/null @@ -1,96 +0,0 @@ -## Same-Currency Transfers - -Use the `/transfer-out` endpoint when sending funds in the same currency (no exchange rate needed). This is the simplest and fastest option for domestic transfers. - -### When to use same-currency transfers - -- Transferring USD from a USD internal account to a USD external account -- Sending funds within the same country using the same payment rail -- No currency conversion is required - -### Create a transfer - - - - Retrieve the internal account (source) and external account (destination) IDs: - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Note the `id` fields from both the internal and external accounts you want to use. - - - - - Create the transfer by specifying the source and destination accounts: - -```bash -curl -X POST 'https://api.lightspark.com/grid/2025-10-13/transfer-out' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H 'Content-Type: application/json' \ - -d '{ - "source": { "accountId": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123" }, - "destination": { "accountId": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" }, - "amount": 12550 - }' -``` - -```json Success (201 Created) -{ - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000015", - "status": "PENDING", - "type": "OUTGOING", - "source": { - "accountId": "InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123", - "currency": "USD" - }, - "destination": { - "accountId": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965", - "currency": "USD" - }, - "sentAmount": { "amount": 12550, "currency": { "code": "USD", "decimals": 2 } }, - "receivedAmount": { "amount": 12550, "currency": { "code": "USD", "decimals": 2 } }, - "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001", - "platformCustomerId": "customer_12345", - "createdAt": "2025-10-03T15:00:00Z", - "settledAt": null -} -``` - - - The `amount` is specified in the smallest unit of the currency (cents for USD, pence for GBP, etc.). For example, `12550` represents $125.50 USD. - - - - - The transaction is created with a `PENDING` status. Monitor the status by: - -**Option 1: Webhook notifications** (recommended) - -```json -{ - "type": "OUTGOING_PAYMENT", - "transaction": { - "id": "Transaction:019542f5-b3e7-1d02-0000-000000000015", - "status": "COMPLETED", - "settledAt": "2025-10-03T15:02:30Z" - }, - "timestamp": "2025-10-03T15:03:00Z" -} -``` - -**Option 2: Poll the transaction endpoint** - -```bash -curl -X GET 'https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000015' \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - - - When the transaction status changes to `COMPLETED`, the funds have been successfully transferred to the external account. - - - - diff --git a/mintlify/snippets/sending/uma.mdx b/mintlify/snippets/sending/uma.mdx deleted file mode 100644 index 9933c55e..00000000 --- a/mintlify/snippets/sending/uma.mdx +++ /dev/null @@ -1,97 +0,0 @@ -## Sending to an UMA Address -Send to an UMA address when the receiver is identified by their UMA handle eg $alice@example.com. You'll look up the receiver, create a quote, and then fund. - -### Look up the recipient - -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/receiver/\$recipient@example.com?platformUserId=9f84e0c2a72c4fa" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -#### Response -```json Success (200 OK) -{ - "id": "Lookup:019542f5-b3e7-1d02-0000-000000000009", - "receiver": { - "handle": "$recipient@example.com", - "vaspName": "ExampleBank", - "network": "UMA" - }, - "supportedCurrencies": [ - { "code": "EUR", "decimals": 2 }, - { "code": "BRL", "decimals": 2 } - ], - "requiredPayerFields": [ - "FULL_NAME", - "BIRTH_DATE" - ], - "constraints": { - "minAmount": { "amount": 100, "currency": { "code": "USD", "decimals": 2 } }, - "maxAmount": { "amount": 10000000, "currency": { "code": "USD", "decimals": 2 } } - }, - "createdAt": "2025-10-03T15:00:00Z" -} -``` -The response includes supported currencies and any required payer information fields. - -If the receiver's VASP requires payer data, include it in `senderUserInfo` (applies to either tab). -### Create a quote - - -```bash Just-in-time funding -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009", - "sendingCurrencyCode": "USD", - "receivingCurrencyCode": "EUR", - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "Invoice #1234 payment", - "senderUserInfo": { "FULL_NAME": "John Sender", "BIRTH_DATE": "1985-06-15" } - }' -``` - -```bash Prefunded (use internal account as source) -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \ - -H "Content-Type: application/json" \ - -d '{ - "lookupId": "Lookup:019542f5-b3e7-1d02-0000-000000000009", - "source": { "accountId": "InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965" }, - "sendingCurrencyCode": "USD", - "receivingCurrencyCode": "EUR", - "lockedCurrencySide": "SENDING", - "lockedCurrencyAmount": 10000, - "description": "UMA payment from prefunded balance" - }' -``` - - -### Execute payment (just-in-time) - -Use the `paymentInstructions` from the quote to instruct your bank to push funds. Include the exact `reference` provided. - -### Execute payment (prefunded) - -Existing internal account balances will be used to fund the payment. Use the lookup Id above to confirm the payment and execute the quote. - -#### Execute the quote - -```bash -curl -X POST "https://api.lightspark.com/grid/2025-10-13/quotes/Quote:019542f5-b3e7-1d02-0000-000000000025/execute" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - -Executing the quote creates a transaction that draws from your internal account and delivers to the recipient associated with the UMA address. - -#### Track status -Listen for `OUTGOING_PAYMENT` webhooks until the transaction reaches `COMPLETED` or `FAILED`. - -You can also query for the transaction with the following snippet: -```bash -curl -X GET "https://api.lightspark.com/grid/2025-10-13/transactions/Transaction:019542f5-b3e7-1d02-0000-000000000030" \ - -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" -``` - diff --git a/mintlify/snippets/terminology.mdx b/mintlify/snippets/terminology.mdx deleted file mode 100644 index 9c525f53..00000000 --- a/mintlify/snippets/terminology.mdx +++ /dev/null @@ -1,135 +0,0 @@ -There are several key entities in the Grid API: **Platform**, **Customers**, **Internal Accounts**, **External Accounts**, **Quotes**, **Transactions**, and **UMA Addresses**. - - -Entity relationships diagram showing platform, customers, internal accounts, external accounts, transactions, and UMA addresses - - -## Businesses, People, and Accounts - -### Platform - -Your **platform** is you! It's the top-level entity that integrates with the Grid API. The platform: - -- Has its own configuration (webhook endpoint, supported currencies, API tokens, etc.) -- A platform can have many customers both business and individual -- Manages multiple customers and their accounts -- Can hold platform-owned internal accounts for settlement and liquidity management -- Acts as the integration point between your application and the open Money Grid - -### Customers - -**Customers** are your end users who send and receive payments through your platform. Each customer: - -- Can be an individual or business entity -- Has a KYC/KYB status that determines their ability to transact. If you are a regulated financial institution, this will typically be `APPROVED` since you do the KYC/KYB yourself. -- Is identified by both a system-generated ID and optionally your platform-specific customer ID -- May have associated internal accounts and external accounts -- May have a unique **UMA address** (e.g., `$john.doe@yourdomain.com`). If you don't assign an UMA address when creating a customer, they will be assigned a system-generated one. - -### Internal Accounts - -**Internal accounts** are Grid-managed accounts that hold balances in specific currencies. They can belong to either: - -- **Platform internal accounts** - Owned by the platform for settlement, liquidity, and float management -- **Customer internal accounts** - Associated with specific customers for holding funds - -Internal accounts: -- Have balances in a single currency (USD, EUR, MXN, etc.) -- Can be funded via bank transfers or crypto deposits using payment instructions -- Are used as sources or destinations for transactions instantly 24/7/365 -- Track available balance for sending payments or receiving funds - -### External Accounts - -**External accounts** are traditional bank accounts, crypto wallets, or other payment instruments connected to customers -for on-ramping or off-ramping funds. Each external account: - -- Are associated with a specific customer or the platform -- Represents a real-world bank account (with routing number, account number, IBAN, etc.), wallet, or payment instrument -- Has an associated beneficiary (individual or business) who receives payments from the customer or platform -- Has a status indicating screening status (ACTIVE, PENDING, INACTIVE, etc.) -- Can be used as a destination for quote-based transfers or same currency transfers like withdrawals -- For pullable sources like debit cards or ACH pulls, an external account can be used as a source for transfers-in to - fund internal accounts or to fund cross-border transfers via quotes. - -## Entity Examples by Use Case - -Understanding how entities map to your specific use case helps clarify your integration architecture. Here are common examples: - -### B2B Payouts Platform (e.g., Bill.com, Routable) - -| Entity Type | Who They Are | Example | -|------------|--------------|---------| -| **Platform** | The payouts platform itself | Your company providing AP automation | -| **Customer** | Businesses sending payments to vendors | Acme Corp (your client company) | -| **External Account** | Vendors/suppliers receiving payments | Office supply vendor, freelance contractor | - -**Flow**: Acme Corp (customer) uses your platform to pay their vendor invoices → funds move from Acme's internal account → to vendor's external bank account - -### Direct Rewards Platform (Platform-Funded Model) - -| Entity Type | Who They Are | Example | -|------------|--------------|---------| -| **Platform** | The app paying rewards directly to users | Your cashback app | -| **Customer** | (Not used in this model) | N/A | -| **External Account** | End users' crypto wallets receiving rewards | Sarah's self-custody Bitcoin wallet | - -**Flow**: Your platform sends micro-payouts directly from platform internal accounts → to users' external crypto wallets at scale. Common for cashback apps where the platform earns affiliate commissions and shares them with users. - -### White-Label Rewards Platform (Customer-Funded Model) - -| Entity Type | Who They Are | Example | -|------------|--------------|---------| -| **Platform** | The rewards infrastructure provider | Your white-label rewards API | -| **Customer** | Brands or merchants running reward campaigns | Nike, Starbucks | -| **External Account** | End users' crypto wallets receiving rewards | Sarah's self-custody Bitcoin wallet | - -**Flow**: Nike (customer) funds their internal account → your platform sends rewards on their behalf → to users' external crypto wallets. Common for brand loyalty programs where merchants manage their own reward budgets. - -### Remittance/P2P App (e.g., Wise, Remitly) - -| Entity Type | Who They Are | Example | -|------------|--------------|---------| -| **Platform** | The remittance service | Your money transfer app | -| **Customer** | Both sender and recipient of funds | Maria (sender in US), Juan (recipient in Mexico) | -| **External Account** | Bank accounts for funding/receiving | Maria's US bank (funding), Juan's Mexican bank (receiving funds) | - -**Flow**: Maria (customer) funds transfer from her external account → to Juan (also a customer) → who receives funds in his external bank account. Alternatively, Maria could send to Juan's UMA address directly. - -## Transactions and Addressing Entities - -### Quotes - -**Quotes** provide locked-in exchange rates and payment instructions for transfers. A quote: - -- Specifies a source (internal account, customer ID, or the platform itself) and destination (internal/external account or UMA address) -- Locks an exchange rate for a short period (typically 1-5 minutes) or can be immediately executed with the `immediatelyExecute` flag -- Calculates total fees and amounts for currency conversion -- Provides payment instructions for funding the transfer if needed, or can be funded via an internal account balance. -- Must be executed before it expires -- Creates a transaction when executed - -### Transactions - -**Transactions** represent completed or in-progress payment transfers. Each transaction: - -- Has a type (INCOMING or OUTGOING from the platform's perspective) -- Has a status (PENDING, COMPLETED, FAILED, etc.) -- References a customer (sender for outgoing, recipient for incoming) or a platform internal account -- Specifies source and destination (accounts or UMA addresses) -- Includes amounts, currencies, and settlement information -- May include counterparty information for compliance purposes if required by your platform configuration - -Transactions are created when: -- A quote is executed (either incoming or outgoing) -- A same currency transfer is initiated (transfer-in or transfer-out) - -### UMA Addresses (optional) - -**UMA addresses** are human-readable payment identifiers that follow the format `$username@domain.com`. They: - -- Uniquely identify entities on the Grid network -- Enable sending and receiving payments across different platforms without knowing the recipient's underlying account details or personal information -- Support currency negotiation and cross-border transfers -- Work similar to email addresses but for payments -- Are an optional UX improvement for some use cases. Use of UMA addresses is not required in order to use the Grid API. diff --git a/mintlify/snippets/uma-test-wallet.mdx b/mintlify/snippets/uma-test-wallet.mdx deleted file mode 100644 index b5a70cb6..00000000 --- a/mintlify/snippets/uma-test-wallet.mdx +++ /dev/null @@ -1,20 +0,0 @@ -The UMA Test Wallet is an external tool that demonstrates UMA payment flows end to end and gives you a realistic counterparty for development and QA. It helps you understand flows through hands-on interaction, explore recommended UX patterns, and develop against a live UMA FI. - - - -Open the hosted test wallet to try UMA flows in your browser. - - - -Browse the code, file issues, and contribute improvements. - - - -### What UMA Test Wallet can do -- **Experience UMA flows**: Send and receive cross currency UMA payments -- **Preview UX best practices**: See recommended entry points, confirmations, and error handling. -- **Develop and test**: Use the wallet as a counterparty FI when building UMA integrations - - -For background on UMA itself, see the UMA Standard: [UMA Standard—Introduction](https://docs.uma.me/uma-standard/introduction). - diff --git a/mintlify/snippets/variables.mdx b/mintlify/snippets/variables.mdx deleted file mode 100644 index f92b673c..00000000 --- a/mintlify/snippets/variables.mdx +++ /dev/null @@ -1,6 +0,0 @@ -export const topLevelProductName = "Grid"; -export const remittanceProductName = "Grid"; -export const payToBankProductName = "Grid"; -export const rewardsProductName = "Grid"; -export const rampsProductName = "Grid"; -export const gridBaseUrl = "https://api.lightspark.com/grid/2025-10-13"; \ No newline at end of file diff --git a/mintlify/snippets/webhooks.mdx b/mintlify/snippets/webhooks.mdx deleted file mode 100644 index b44a1741..00000000 --- a/mintlify/snippets/webhooks.mdx +++ /dev/null @@ -1,205 +0,0 @@ -All webhooks sent by the Grid API include a signature in the `X-Grid-Signature` header, which allows you to verify the authenticity of the webhook. This is critical for security, as it ensures that only legitimate webhooks from Grid are processed by your system. - -## Signature Verification Process - -1. **Obtain your Grid public key** - - This is provided to you during the integration process. Reach out to us at [support@lightspark.com](mailto:support@lightspark.com) or over Slack to get the public key. - - The key is in PEM format and can be used with standard cryptographic libraries - -2. **Verify incoming webhooks** - - Extract the signature from the `X-Grid-Signature` header - - Decode the base64 signature - - Create a SHA-256 hash of the entire request body - - Verify the signature using the Grid webhook public key and the hash - - Only process the webhook if the signature verification succeeds - -## Verification Examples - -### Node.js Example - -```javascript -const crypto = require('crypto'); -const express = require('express'); -const app = express(); - -// Your Grid public key provided during integration -const GRID_WEBHOOK_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE... ------END PUBLIC KEY-----`; - -app.post('/webhooks/uma', (req, res) => { - const signatureHeader = req.header('X-Grid-Signature'); - - if (!signatureHeader) { - return res.status(401).json({ error: 'Signature missing' }); - } - - try { - let signature: Buffer; - try { - // Parse the signature as JSON. It's in the format {"v": "1", "s": "base64_signature"} - const signatureObj = JSON.parse(signatureHeader); - if (signatureObj.v && signatureObj.s) { - // The signature is in the 's' field - signature = Buffer.from(signatureObj.s, "base64"); - } else { - throw new Error("Invalid JSON signature format"); - } - } catch { - // If JSON parsing fails, treat as direct base64 - signature = Buffer.from(signatureHeader, "base64"); - } - - // Create verifier with the public key and correct algorithm - const verifier = crypto.createVerify("SHA256"); - const payload = await request.text(); - verifier.update(payload); - verifier.end(); - - // Verify the signature using the webhook public key - const isValid = verifier.verify( - { - key: GRID_WEBHOOK_PUBLIC_KEY, - format: "pem", - type: "spki", - }, - signature, - ); - - if (!isValid) { - return res.status(401).json({ error: 'Invalid signature' }); - } - - // Webhook is verified, process it based on type - const webhookData = req.body; - - if (webhookData.type === 'INCOMING_PAYMENT') { - // Process incoming payment webhook - // ... - } else if (webhookData.type === 'OUTGOING_PAYMENT') { - // Process outgoing payment webhook - // ... - } - - // Acknowledge receipt of the webhook - return res.status(200).json({ received: true }); - } catch (error) { - console.error('Signature verification error:', error); - return res.status(401).json({ error: 'Signature verification failed' }); - } -}); - -app.listen(3000, () => { - console.log('Webhook server listening on port 3000'); -}); -``` - -### Python Example - -```python -from cryptography.hazmat.primitives import serialization, hashes -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature -from flask import Flask, request, jsonify -import base64 - -app = Flask(__name__) - -# Your Grid public key provided during integration -GRID_PUBLIC_KEY = """-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE... ------END PUBLIC KEY-----""" - -# Load the public key -public_key = serialization.load_pem_public_key( - GRID_PUBLIC_KEY.encode('utf-8') -) - -@app.route('/webhooks/uma', methods=['POST']) -def handle_webhook(): - # Get signature from header - signature = request.headers.get('X-Grid-Signature') - if not signature: - return jsonify({'error': 'Signature missing'}), 401 - - try: - # Get the raw request body - request_body = request.get_data() - - # Create a SHA-256 hash of the request body - hash_obj = hashes.Hash(hashes.SHA256()) - hash_obj.update(request_body) - digest = hash_obj.finalize() - - # Decode the base64 signature - signature_bytes = base64.b64decode(signature) - - # Verify the signature - try: - public_key.verify( - signature_bytes, - request_body, - ec.ECDSA(hashes.SHA256()) - ) - except Exception as e: - return jsonify({'error': 'Invalid signature'}), 401 - - # Webhook is verified, process it based on type - webhook_data = request.json - - if webhook_data['type'] == 'INCOMING_PAYMENT': - # Process incoming payment webhook - # ... - pass - elif webhook_data['type'] == 'OUTGOING_PAYMENT': - # Process outgoing payment webhook - # ... - pass - - # Acknowledge receipt of the webhook - return jsonify({'received': True}), 200 - except Exception as e: - print(f'Signature verification error: {e}') - return jsonify({'error': 'Signature verification failed'}), 401 - -if __name__ == '__main__': - app.run(port=3000) -``` - -## Testing - -To test your webhook implementation, you can trigger a test webhook from the Grid dashboard. This will send a test webhook to the endpoint you provided during the integration process. The test webhook will also be sent automatically when you update your platform configuration with a new webhook URL. - -An example of the test webhook payload is shown below: - -```json -{ - "test": true, - "timestamp": "2023-08-15T14:32:00Z", - "webhookId": "Webhook:019542f5-b3e7-1d02-0000-000000000007", - "type": "TEST" -} -``` - -You should verify the signature of the webhook using the Grid public key and the process outlined in the [Signature Verification Process](#signature-verification-process) section and then reply with a 200 OK response to acknowledge receipt of the webhook. - -## Security Considerations - -- **Always verify signatures**: Never process webhooks without verifying their signatures. -- **Use HTTPS**: Ensure your webhook endpoint uses HTTPS to prevent man-in-the-middle attacks. -- **Implement idempotency**: Use the `webhookId` field to prevent processing duplicate webhooks. -- **Timeout handling**: Implement proper timeout handling and respond to webhooks promptly. - -## Retry Policy - -The Grid API will retry webhooks with the following policy based on the webhook type: - -| Webhook Type | Retry Policy | Notes | -|-------------|-------------|-------| -| TEST | No retries | Used for testing webhook configuration | -| OUTGOING_PAYMENT | Retry with exponential backoff up to 7 days with maximum interval of 30 mins | No retry on 409 (duplicate webhooks) | -| INCOMING_PAYMENT | Retry with exponential backoff up to 7 days with maximum interval of 30 mins | No retry on: 409 (duplicate webhook) or PENDING status since it is served as an approval mechanism in-flow | -| BULK_UPLOAD | Retry with exponential backoff up to 7 days with maximum interval of 30 mins | No retry on 409 (duplicate webhooks) | -| INVITATION_CLAIMED | Retry with exponential backoff up to 7 days with maximum interval of 30 mins | No retry on 409 (duplicate webhooks) | -| KYC_STATUS | Retry with exponential backoff up to 7 days with maximum interval of 30 mins | No retry on 409 (duplicate webhooks) | -| ACCOUNT_STATUS | Retry with exponential backoff up to 7 days with maximum interval of 30 mins | No retry on 409 (duplicate webhooks) | \ No newline at end of file diff --git a/mintlify/styles/fonts.css b/mintlify/styles/fonts.css deleted file mode 100644 index 945ca28e..00000000 --- a/mintlify/styles/fonts.css +++ /dev/null @@ -1,61 +0,0 @@ -/* =========================================== - Suisse Intl Font Family - =========================================== */ - -/* Regular (400) - Default body text */ -@font-face { - font-family: "Suisse Intl"; - src: url("/fonts/suisse-intl/SuisseIntl-Regular.woff2") format("woff2"); - font-weight: 400; - font-style: normal; - font-display: block; -} - -/* Book (450) - Button text */ -@font-face { - font-family: "Suisse Intl"; - src: url("/fonts/suisse-intl/SuisseIntl-Book.woff2") format("woff2"); - font-weight: 450; - font-style: normal; - font-display: block; -} - -/* Medium (500) - Bold/emphasis text */ -@font-face { - font-family: "Suisse Intl"; - src: url("/fonts/suisse-intl/SuisseIntl-Medium.woff2") format("woff2"); - font-weight: 500; - font-style: normal; - font-display: block; -} - -/* Map bold (700) to Medium for consistent typography */ -@font-face { - font-family: "Suisse Intl"; - src: url("/fonts/suisse-intl/SuisseIntl-Medium.woff2") format("woff2"); - font-weight: 700; - font-style: normal; - font-display: block; -} - -/* =========================================== - Suisse Intl Mono Font Family - =========================================== */ - -/* Mono Regular - Code blocks */ -@font-face { - font-family: "Suisse Intl Mono"; - src: url("/fonts/suisse-intl-mono/SuisseIntlMono-Regular-WebXL.woff2") format("woff2"); - font-weight: 400; - font-style: normal; - font-display: block; -} - -/* Mono Bold - Bold code */ -@font-face { - font-family: "Suisse Intl Mono"; - src: url("/fonts/suisse-intl-mono/SuisseIntlMono-Bold-WebXL.woff2") format("woff2"); - font-weight: 700; - font-style: normal; - font-display: block; -} diff --git a/mintlify/styles/styles.css b/mintlify/styles/styles.css deleted file mode 100644 index 0b9e4a0d..00000000 --- a/mintlify/styles/styles.css +++ /dev/null @@ -1,342 +0,0 @@ -/* Import design tokens */ -@import "./tokens.css"; - -/* =========================================== - Custom Font Styles for Suisse Intl - - Note: Base fonts (body/heading) are configured - in docs.json using Mintlify's fonts config. - This CSS handles additional customizations. - =========================================== */ - -/* Global: Enable single-story 'a' (stylistic alternates) */ -* { - font-feature-settings: "salt" on; - text-shadow: none; -} - -/* Headings & Bold: Use Medium weight (500), normal tracking */ -h1, h2, h3, h4, h5, h6, -strong, b, th, -.sidebar-group-header, -.eyebrow, -.font-semibold, -[data-component-part="step-title"], -[data-component-part="tab-button"] { - font-weight: 500 !important; - letter-spacing: normal !important; -} - -/* Navigation tabs, navbar links & CTA button: Medium weight */ -.nav-tabs-item, -.navbar-link a, -#topbar-cta-button, #topbar-cta-button *, -#page-context-menu-button, #page-context-menu-button *, -#pagination a, #pagination a * { - font-weight: 500 !important; - text-shadow: none !important; -} - -/* Active sidebar item only: Medium weight (has text-primary class) */ -a[class*="text-primary"]:not(.nav-tabs-item), -a[class*="text-primary"]:not(.nav-tabs-item) *, -button[class*="text-primary"], -button[class*="text-primary"] * { - font-weight: 500 !important; -} - -/* Code: Use Suisse Intl Mono */ -code, pre, kbd, samp, .font-mono { - font-family: "Suisse Intl Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important; - font-feature-settings: "salt" on !important; -} - -/* API field names: Mono Bold */ -[data-component-part="field-name"] { - font-family: "Suisse Intl Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important; - font-weight: 700 !important; - font-feature-settings: "salt" on !important; -} - -/* Homepage: Headings use Regular weight (400) */ -.hero h1, .hero h2, .hero h3, -.hero .title, .hero .highlight, -.usecase h2, .usecase .highlight, -.hero-card h3, .card-content h3 { - font-weight: 400 !important; -} - -/* Hero title letter-spacing */ -.hero .title span { - letter-spacing: -1.2px !important; -} - -/* =========================================== - Existing Styles - =========================================== */ - -html.light .hero { - background: var(--ls-gray-100); - background-image: radial-gradient( - circle, - var(--ls-black-10) 1px, - transparent 1px - ); - background-size: 24px 24px; - background-position: 0 0; - background-repeat: repeat; - border-bottom: 1px solid var(--ls-black-10); - border-top: 1px solid var(--ls-black-10); -} - -html .hero { - background: var(--ls-gray-950); - background-image: radial-gradient(circle, var(--ls-gray-850) 1px, transparent 1px); - background-size: 24px 24px; - background-position: 0 0; - background-repeat: repeat; - border-bottom: 1px solid var(--ls-white-04); - border-top: 1px solid var(--ls-white-04); -} - -.blue-lines { - background-image: url("/images/blue-lines-sm.svg"); - background-position: top left; - background-repeat: no-repeat; - background-size: 100% 100%; - pointer-events: none; -} - -@media (min-width: 640px) { - .blue-lines { - background-image: url("/images/blue-lines.svg"); - background-position: center center; - background-size: auto; - } -} - -html.light .title { - color: var(--ls-gray-950); -} - -html .title { - color: var(--ls-gray-050); -} - -html.light .usecase { - background: var(--ls-gray-050); -} - -html.light .highlight { - color: var(--ls-gray-950); -} - -html .highlight { - color: var(--ls-gray-050); -} -html.light .hero-card { - background: var(--ls-white); -} - -html.dark .hero-card { - background: var(--ls-gray-950); -} - -.hero .call-to-action { - cursor: pointer; - background: var(--ls-sky-500); - color: var(--ls-gray-050); - display: flex; - height: 40px; - padding: 8px 16px; - justify-content: center; - align-items: center; - gap: 4px; - border-radius: var(--ls-radius-xs); - font-size: 16px; - font-style: normal; - font-weight: 500; /* Medium weight for buttons */ - line-height: 24px; -} - -.hero { - padding-top: 64px; - padding-bottom: 64px; - padding-left: 16px; - padding-right: 16px; - margin-bottom: 32px; - gap: 32px; -} - -@media (min-width: 640px) { - .hero { - padding-top: 96px; - padding-bottom: 96px; - padding-left: 32px; - padding-right: 32px; - margin-bottom: 48px; - gap: 48px; - } -} - -@media (min-width: 1024px) { - .hero { - padding-top: 128px; - padding-bottom: 128px; - padding-left: 120px; - padding-right: 120px; - margin-bottom: 64px; - gap: 64px; - } -} - -.hero .subtext { - width: 100%; - max-width: 538px; - color: var(--ls-gray-400); - text-align: center; - font-feature-settings: "salt" on; - font-size: 18px; - font-style: normal; - font-weight: 400; - line-height: 20px; - letter-spacing: -0.6px; -} - -@media (min-width: 640px) { - .hero .subtext { - font-size: 18px; - line-height: 22px; - } -} - -@media (min-width: 1024px) { - .hero .subtext { - font-size: 20px; - line-height: 24px; - } -} - -.usecase { - display: flex; - padding: 32px 16px; - flex-direction: column; - align-items: flex-start; - gap: 32px; - align-self: stretch; -} - -@media (min-width: 640px) { - .usecase { - padding: 48px 32px; - gap: 40px; - } -} - -@media (min-width: 1024px) { - .usecase { - padding: 64px 120px; - gap: 48px; - } -} - -.usecase h2 { - font-size: 24px; - font-style: normal; - font-weight: 400; - line-height: 32px; /* 133.333% */ - letter-spacing: -0.72px; -} - -.section { - display: flex; - width: 100%; - max-width: 1200px; - flex-direction: column; - align-items: flex-start; - gap: 32px; -} - -@media (min-width: 640px) { - .section { - gap: 40px; - } -} - -@media (min-width: 1024px) { - .section { - gap: 48px; - } -} - -.footer { - display: flex; - flex-direction: row; - align-items: center; - gap: 16px; -} - -@media (min-width: 640px) { - .footer { - gap: 20px; - } -} - -@media (min-width: 1024px) { - .footer { - gap: 24px; - } -} - -.hero-card { - display: flex; - padding: 16px; - flex-direction: column; - align-items: flex-start; - align-self: stretch; - border-radius: var(--ls-radius-xs); - min-height: 200px; -} - -@media (min-width: 640px) { - .hero-card { - padding: 20px; - min-height: 220px; - } -} - -@media (min-width: 1024px) { - .hero-card { - padding: 24px; - min-height: 240px; - } -} - -.card-content { - margin-top: 24px; - font-size: 18px; - font-style: normal; - font-weight: 400; - line-height: 24px; - letter-spacing: -0.6px; -} - -@media (min-width: 640px) { - .card-content { - margin-top: 20px; - font-size: 18px; - line-height: 22px; - } -} - -@media (min-width: 1024px) { - .card-content { - margin-top: 24px; - font-size: 20px; - line-height: 24px; - } -} - -#footer > div.flex.items-center.justify-between > div > a { - display: none; -} diff --git a/mintlify/styles/tokens.css b/mintlify/styles/tokens.css deleted file mode 100644 index 27de4858..00000000 --- a/mintlify/styles/tokens.css +++ /dev/null @@ -1,90 +0,0 @@ -/* =========================================== - Lightspark Design Tokens - - Prefixed with "ls-" to avoid collision with - Mintlify's built-in CSS variables (--gray-XXX). - - Values from Figma design system. - Spacing uses Tailwind classes (p-4, gap-6, etc.) - - Note: docs.json uses these same hex values for - Mintlify theming (JSON can't reference CSS vars). - Keep them in sync when updating colors. - =========================================== */ - -:root { - /* Base Colors */ - --ls-white: #FFFFFF; - --ls-black: #000000; - - /* Brand Colors */ - --ls-blue-800: #004D92; - --ls-blue-500: #0091FF; - --ls-sky-500: #00B3E0; - --ls-teal-500: #44DDB5; - --ls-green-600: #11A967; - --ls-yellow-600: #E09000; - --ls-orange-500: #F77D26; - --ls-pink-800: #A90087; - --ls-pink-500: #FF21CC; - - /* Gray Scale */ - --ls-gray-025: #FAFAF9; - --ls-gray-050: #F8F8F7; - --ls-gray-075: #F4F4F3; - --ls-gray-100: #F0F0EE; - --ls-gray-150: #EBEBE9; - --ls-gray-200: #DEDED9; - --ls-gray-300: #C1C0B8; - --ls-gray-400: #989898; - --ls-gray-500: #7C7C7C; - --ls-gray-600: #656565; - --ls-gray-700: #464646; - --ls-gray-800: #3D3D3D; - --ls-gray-850: #333333; - --ls-gray-900: #282828; - --ls-gray-925: #212121; - --ls-gray-950: #1A1A1A; - --ls-gray-975: #111111; - - /* Alpha White */ - --ls-white-02: rgba(255, 255, 255, 0.02); - --ls-white-04: rgba(255, 255, 255, 0.04); - --ls-white-06: rgba(255, 255, 255, 0.06); - --ls-white-10: rgba(255, 255, 255, 0.1); - --ls-white-20: rgba(255, 255, 255, 0.2); - --ls-white-30: rgba(255, 255, 255, 0.3); - --ls-white-40: rgba(255, 255, 255, 0.4); - --ls-white-50: rgba(255, 255, 255, 0.5); - --ls-white-60: rgba(255, 255, 255, 0.6); - --ls-white-70: rgba(255, 255, 255, 0.7); - --ls-white-80: rgba(255, 255, 255, 0.8); - --ls-white-90: rgba(255, 255, 255, 0.9); - - /* Alpha Black */ - --ls-black-02: rgba(0, 0, 0, 0.02); - --ls-black-04: rgba(0, 0, 0, 0.04); - --ls-black-06: rgba(0, 0, 0, 0.06); - --ls-black-10: rgba(0, 0, 0, 0.1); - --ls-black-20: rgba(0, 0, 0, 0.2); - --ls-black-30: rgba(0, 0, 0, 0.3); - --ls-black-40: rgba(0, 0, 0, 0.4); - --ls-black-50: rgba(0, 0, 0, 0.5); - --ls-black-60: rgba(0, 0, 0, 0.6); - --ls-black-70: rgba(0, 0, 0, 0.7); - --ls-black-80: rgba(0, 0, 0, 0.8); - --ls-black-90: rgba(0, 0, 0, 0.9); - - /* Border Radius */ - --ls-radius-none: 0px; - --ls-radius-2xs: 2px; - --ls-radius-xs: 4px; - --ls-radius-sm: 6px; - --ls-radius-md: 8px; - --ls-radius-lg: 12px; - --ls-radius-xl: 16px; - --ls-radius-2xl: 24px; - --ls-radius-3xl: 32px; - --ls-radius-4xl: 40px; - --ls-radius-round: 999px; -} diff --git a/openapi.yaml b/openapi.yaml deleted file mode 100644 index 90d2e8af..00000000 --- a/openapi.yaml +++ /dev/null @@ -1,6897 +0,0 @@ -openapi: 3.1.0 -info: - title: Grid API - description: | - API for managing global payments on the open Money Grid. Built by Lightspark. See the full documentation at https://grid.lightspark.com/. - version: '2025-10-13' - contact: - name: Lightspark Support - email: support@lightspark.com - license: - name: Proprietary - url: https://lightspark.com/terms -servers: - - url: https://api.lightspark.com/grid/2025-10-13 - description: Production server -security: - - BasicAuth: [] -tags: - - name: Platform Configuration - description: Platform configuration endpoints for managing global settings. You can also configure these settings in the Grid dashboard. - - name: Customers - description: Customer management endpoints for creating and updating customer information - - name: Internal Accounts - description: Internal account management endpoints for creating and managing internal accounts - - name: External Accounts - description: External account management endpoints for creating and managing external bank accounts - - name: Same-Currency Transfers - description: Endpoints for transferring funds between internal and external accounts with the same currency - - name: Cross-Currency Transfers - description: Endpoints for creating and confirming quotes for cross-currency transfers - - name: Transactions - description: Endpoints for retrieving transaction information - - name: Webhooks - description: Webhook endpoints and configuration for receiving notifications - - name: Invitations - description: Endpoints for creating, claiming and managing UMA invitations - - name: Sandbox - description: Endpoints to trigger test cases in sandbox - - name: API Tokens - description: Endpoints to programmatically manage API tokens -paths: - /config: - get: - summary: Get platform configuration - description: Retrieve the current platform configuration - operationId: getPlatformConfig - tags: - - Platform Configuration - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/PlatformConfig' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - patch: - summary: Update platform configuration - description: Update the platform configuration settings - operationId: updatePlatformConfig - tags: - - Platform Configuration - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - umaDomain: - type: string - example: mycompany.com - webhookEndpoint: - type: string - example: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - type: array - items: - $ref: '#/components/schemas/PlatformCurrencyConfig' - example: - umaDomain: mycompany.com - webhookEndpoint: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - - currencyCode: USD - minAmount: 100 - maxAmount: 1000000 - enabledTransactionTypes: - - OUTGOING - - INCOMING - requiredCounterpartyFields: - - name: FULL_NAME - mandatory: true - - name: NATIONALITY - mandatory: true - - name: BIRTH_DATE - mandatory: true - responses: - '200': - description: Configuration updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/PlatformConfig' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - '501': - description: Not implemented - content: - application/json: - schema: - $ref: '#/components/schemas/Error501' - /customers: - post: - summary: Add a new customer - description: Register a new customer in the system with an account identifier and bank account information - operationId: createCustomer - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CustomerCreateRequestOneOf' - responses: - '201': - description: Customer created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/CustomerOneOf' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Customer with the UMA address already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - '501': - description: Not implemented - content: - application/json: - schema: - $ref: '#/components/schemas/Error501' - get: - summary: List customers - description: | - Retrieve a list of customers with optional filtering parameters. Returns all customers that match - the specified filters. If no filters are provided, returns all customers (paginated). - operationId: listCustomers - tags: - - Customers - security: - - BasicAuth: [] - parameters: - - name: platformCustomerId - in: query - description: Filter by platform-specific customer identifier - required: false - schema: - type: string - - name: customerType - in: query - description: Filter by customer type - required: false - schema: - $ref: '#/components/schemas/CustomerType' - - name: createdAfter - in: query - description: Filter customers created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter customers created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedAfter - in: query - description: Filter customers updated after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedBefore - in: query - description: Filter customers updated before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: umaAddress - in: query - description: Filter by uma address - required: false - schema: - type: string - - name: isIncludingDeleted - in: query - description: Whether to include deleted customers in the results. Default is false. - required: false - schema: - type: boolean - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of customers matching the filter criteria - items: - $ref: '#/components/schemas/CustomerOneOf' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of customers matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/{customerId}: - parameters: - - name: customerId - in: path - description: System-generated unique customer identifier - required: true - schema: - type: string - get: - summary: Get customer by ID - description: Retrieve a customer by their system-generated ID - operationId: getCustomerById - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - patch: - summary: Update customer by ID - description: Update a customer's metadata by their system-generated ID - operationId: updateCustomerById - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CustomerUpdateRequestOneOf' - examples: - individualUpdate: - summary: Update individual customer example - value: - customerType: INDIVIDUAL - fullName: John Smith - birthDate: '1985-06-15' - address: - line1: 456 Market St - city: San Francisco - state: CA - postalCode: '94103' - country: US - businessUpdate: - summary: Update business customer example - value: - customerType: BUSINESS - businessInfo: - legalName: New Tech Solutions LLC - registrationNumber: BRN-987654321 - taxId: EIN-123456789 - address: - line1: 100 Technology Parkway - city: Palo Alto - state: CA - postalCode: '94304' - country: US - responses: - '200': - description: Customer updated successfully - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - delete: - summary: Delete customer by ID - description: Delete a customer by their system-generated ID - operationId: deleteCustomerById - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Customer deleted successfully - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '410': - description: Customer deleted already - content: - application/json: - schema: - $ref: '#/components/schemas/Error410' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/kyc-link: - parameters: - - name: redirectUri - in: query - description: An optional uri a customer will be redirected to after completing the hosted KYC flow - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: The platform id of the customer to onboard - required: true - schema: - type: string - get: - summary: Get a KYC link for onboarding a customer - description: Generate a hosted KYC link to onboard a customer - operationId: getKycLinkForCustomer - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - properties: - kycUrl: - type: string - description: A hosted KYC link for your customer to complete KYC - example: https://example.com/redirect - platformCustomerId: - type: string - description: The platform id of the customer to onboard - example: 019542f5-b3e7-1d02-0000-000000000001 - customerId: - type: string - description: The customer id of the newly created customer on the system - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/internal-accounts: - get: - summary: List Customer internal accounts - description: | - Retrieve a list of internal accounts with optional filtering parameters. Returns all - internal accounts that match the specified filters. If no filters are provided, returns all internal accounts - (paginated). - - Internal accounts are created automatically when a customer is created based on the platform configuration. - operationId: listCustomerInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by internal accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: '#/components/schemas/InternalAccount' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of customers matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /platform/internal-accounts: - get: - summary: List platform internal accounts - description: | - Retrieve a list of all internal accounts that belong to the platform, as opposed to an individual customer. - - These accounts are created automatically when the platform is configured for each supported currency. They can be used for things like distributing bitcoin rewards to customers, or for other platform-wide purposes. - operationId: listPlatformInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: '#/components/schemas/InternalAccount' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/external-accounts: - get: - summary: List Customer external accounts - description: | - Retrieve a list of external accounts with optional filtering parameters. Returns all - external accounts that match the specified filters. If no filters are provided, returns all external accounts - (paginated). - - External accounts are bank accounts, cryptocurrency wallets, or other payment destinations that customers can use to receive funds from the platform. - operationId: listCustomerExternalAccounts - tags: - - External Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by external accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: '#/components/schemas/ExternalAccount' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of external accounts matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - post: - summary: Add a new external account - description: |- - Register a new external bank account for a customer. - - **Sandbox Testing:** In sandbox mode, use these account number patterns to test different transfer scenarios. These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createCustomerExternalAccount - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccountCreateRequest' - examples: - usBankAccount: - summary: Create external US bank account - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: USD - accountInfo: - accountType: US_ACCOUNT - accountNumber: '12345678901' - routingNumber: '123456789' - accountCategory: CHECKING - bankName: Chase Bank - platformAccountId: ext_acc_123456 - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: '1990-01-15' - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: '94105' - country: US - sparkWallet: - summary: Create external Spark wallet - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - responses: - '201': - description: External account created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - External account already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /platform/external-accounts: - get: - summary: List platform external accounts - description: | - Retrieve a list of all external accounts that belong to the platform, as opposed to an individual customer. - - These accounts are used for platform-wide operations such as receiving funds from external sources or managing platform-level payment destinations. - operationId: listPlatformExternalAccounts - tags: - - External Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - post: - summary: Add a new platform external account - description: |- - Register a new external bank account for the platform. - - **Sandbox Testing:** In sandbox mode, use these account number patterns to test different transfer scenarios. These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createPlatformExternalAccount - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccountCreateRequest' - examples: - usBankAccount: - summary: Create external US bank account - value: - currency: USD - accountInfo: - accountType: US_ACCOUNT - accountNumber: '12345678901' - routingNumber: '123456789' - accountCategory: CHECKING - bankName: Chase Bank - platformAccountId: ext_acc_123456 - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: '1990-01-15' - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: '94105' - country: US - sparkWallet: - summary: Create external Spark wallet - value: - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - responses: - '201': - description: External account created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - External account already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /plaid/link-tokens: - post: - summary: Request Plaid Link token - description: | - Creates a Plaid Link token that can be used to initialize Plaid Link in your application. - The Link token is used to authenticate the customer and allow them to select their bank account. - - **Async Flow:** - 1. Platform calls this endpoint to get a link_token and callbackUrl - 2. Platform displays Plaid Link UI to the end customer using the link_token - 3. End customer authenticates with their bank and selects an account - 4. Plaid returns a public_token to the platform - 5. Platform POSTs the public_token to the callbackUrl - 6. Lightspark exchanges the public_token with Plaid and creates the external account asynchronously - 7. Platform receives a webhook notification when the external account is ready - operationId: createPlaidLinkToken - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PlaidLinkTokenRequest' - example: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - responses: - '200': - description: Link token created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/PlaidLinkTokenResponse' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /plaid/callback/{plaid_link_token}: - post: - summary: Submit Plaid public token - description: | - After the customer completes Plaid Link authentication, the platform should POST - the public_token to this callback URL (provided in the link token response). - - This will trigger asynchronous processing: - 1. Lightspark exchanges the public_token for an access_token with Plaid - 2. Lightspark retrieves and verifies the account details - 3. An external account is created - 4. A webhook notification is sent to the platform when complete - operationId: submitPlaidPublicToken - tags: - - External Accounts - parameters: - - name: plaid_link_token - in: path - required: true - description: The Plaid link token from the link token response, used to identify the session - schema: - type: string - example: link-sandbox-abc123xyz-1234-5678 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PlaidCallbackRequest' - example: - publicToken: public-sandbox-12345678-1234-1234-1234-123456789012 - accountId: plaid_account_id_123 - responses: - '202': - description: | - A pending external account resource will be created and returned while the Grid API asynchronously processes the Plaid public token. - content: - application/json: - schema: - $ref: '#/components/schemas/ExternalAccount' - '400': - description: Bad request - Invalid public token or link token - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '404': - description: Link token not found or expired - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Link token already used - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transfer-in: - post: - summary: Create a transfer-in request - description: | - Transfer funds from an external account to an internal account for a specific customer. This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). Otherwise, use the paymentInstructions on the internal account to deposit funds. - operationId: createTransferIn - tags: - - Same-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - source - - destination - properties: - source: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an external account ID - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Source external account details - destination: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an internal account ID - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination internal account details - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - examples: - transferIn: - summary: Transfer from external to internal account - value: - source: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - amount: 12550 - responses: - '201': - description: Transfer-in request created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Transaction' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer or account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transfer-out: - post: - summary: Create a transfer-out request - description: | - Transfer funds from an internal account to an external account for a specific customer. - operationId: createTransferOut - tags: - - Same-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: | - A unique identifier for the request. If the same key is sent multiple times, the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - source - - destination - properties: - source: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an internal account ID - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Source internal account details - destination: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an external account ID - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Destination external account details - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - examples: - transferOut: - summary: Transfer from internal to external account - value: - source: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - destination: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - amount: 12550 - responses: - '201': - description: Transfer-out request created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Transaction' - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Customer or account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /receiver/uma/{receiverUmaAddress}: - get: - summary: Look up an UMA address for payment - description: | - Lookup a receiving UMA address to determine supported currencies and exchange rates. - This endpoint helps platforms determine what currencies they can send to a given UMA address. - operationId: lookupUma - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: receiverUmaAddress - in: path - description: UMA address of the intended recipient - required: true - schema: - type: string - - name: senderUmaAddress - in: query - description: UMA address of the sender (optional if customerId is provided) - required: false - schema: - type: string - - name: customerId - in: query - description: System ID of the sender (optional if senderUmaAddress is provided) - required: false - schema: - type: string - responses: - '200': - description: Successful lookup - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ReceiverLookupResponse' - - type: object - required: - - receiverUmaAddress - properties: - receiverUmaAddress: - type: string - description: The UMA address that was looked up - example: $receiver@uma.domain - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: UMA address not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /receiver/external-account/{accountId}: - get: - summary: Look up an external account for payment - description: | - Lookup an external account by ID to determine supported currencies and exchange rates. - This endpoint helps platforms determine what currencies they can send to a given external account, along with the current estimated exchange rates and minimum and maximum amounts that can be sent. - operationId: lookupExternalAccount - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: accountId - in: path - description: System-generated ID of the external account - required: true - schema: - type: string - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - - name: senderUmaAddress - in: query - description: UMA address of the sender (optional if customerId is provided) - required: false - schema: - type: string - - name: customerId - in: query - description: System ID of the sender (optional if senderUmaAddress is provided) - required: false - schema: - type: string - responses: - '200': - description: Successful lookup - content: - application/json: - schema: - allOf: - - $ref: '#/components/schemas/ReceiverLookupResponse' - - type: object - required: - - accountId - properties: - accountId: - type: string - description: The external account ID that was looked up - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: External account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /quotes/{quoteId}: - get: - summary: Get quote by ID - description: | - Retrieve a quote by its ID. If the quote has been settled, it will include - the transaction ID. This allows clients to track the full lifecycle of a payment - from quote creation to settlement. - operationId: getQuoteById - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: quoteId - in: path - description: ID of the quote to retrieve - required: true - schema: - type: string - responses: - '200': - description: Quote retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /quotes: - post: - summary: Create a transfer quote - description: | - Generate a quote for a cross-currency transfer between any combination of accounts - and UMA addresses. This endpoint handles currency exchange and provides the necessary - instructions to execute the transfer. - - **Transfer Types Supported:** - - **Account to Account**: Transfer between internal/external accounts with currency exchange. - - **Account to UMA**: Transfer from an internal account to an UMA address. - - **UMA to Account or UMA to UMA**: This transfer type will only be funded by payment instructions, not from an internal account. - - **Key Features:** - - **Flexible Amount Locking**: Always specify whether you want to lock the sending amount or receiving amount - - **Currency Exchange**: Handles all cross-currency transfers with real-time exchange rates - - **Payment Instructions**: For UMA or customer ID sources, provides banking details needed for execution - - **Important:** If you are transferring funds in the same currency (no exchange required), - use the `/transfer-in` or `/transfer-out` endpoints instead. - - **Sandbox Testing:** When using the `externalAccountDetails` destination type in sandbox mode, use account number patterns ending in specific digits to test different scenarios. - These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. - For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createQuote - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/QuoteRequest' - examples: - accountToAccount: - summary: Account to Account Transfer - value: - source: - accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - currency: EUR - lockedCurrencySide: SENDING - lockedCurrencyAmount: 10000 - description: Transfer between accounts, either internal or external. - accountToUma: - summary: Account to UMA Address Transfer - value: - lookupId: LookupRequest:019542f5-b3e7-1d02-0000-000000000009 - source: - accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - umaAddress: $receiver@uma.domain.com - currency: EUR - lockedCurrencySide: SENDING - lockedCurrencyAmount: 1000 - description: 'Payment for invoice #1234' - realTimeFundingToSparkWallet: - summary: Real-time funding to Spark Wallet as an on-ramp flow. Immediate execution. - value: - source: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000009 - currency: USD - destination: - externalAccountDetails: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - lockedCurrencySide: RECEIVING - lockedCurrencyAmount: 10000 - immediatelyExecute: true - description: Bitcoin reward payout! - responses: - '201': - description: | - Transfer quote created successfully. The response includes exchange rates, - fees, and transfer details. For transfers involving UMA addresses, payment - instructions are also included for execution through banking systems. - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: '#/components/schemas/Error412' - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: '#/components/schemas/Error424' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - '501': - description: Not implemented - content: - application/json: - schema: - $ref: '#/components/schemas/Error501' - get: - summary: List transfer quotes - description: | - Retrieve a list of transfer quotes with optional filtering parameters. Returns all - quotes that match the specified filters. If no filters are provided, returns all quotes - (paginated). - operationId: listQuotes - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: customerId - in: query - description: Filter by sending customer ID - required: false - schema: - type: string - - name: sendingAccountId - in: query - description: Filter by sending account ID - required: false - schema: - type: string - - name: receivingAccountId - in: query - description: Filter by receiving account ID - required: false - schema: - type: string - - name: sendingUmaAddress - in: query - description: Filter by sending UMA address - required: false - schema: - type: string - - name: receivingUmaAddress - in: query - description: Filter by receiving UMA address - required: false - schema: - type: string - - name: status - in: query - description: Filter by quote status - required: false - schema: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - - name: createdAfter - in: query - description: Filter quotes created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter quotes created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of quotes matching the criteria - items: - $ref: '#/components/schemas/Quote' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of quotes matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /quotes/{quoteId}/execute: - post: - summary: Execute a quote - description: | - Execute a quote by its ID. This endpoint initiates the transfer between - the source and destination accounts. - - This endpoint can only be used for quotes with a `source` which is either an internal account, - or has direct pull functionality (e.g. ACH pull with an external account). - - Once executed, the quote cannot be cancelled and the transfer will be processed. - operationId: executeQuote - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: quoteId - in: path - required: true - description: The unique identifier of the quote to execute - schema: - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - responses: - '200': - description: | - Quote confirmed successfully. The transfer has been initiated and - the quote status has been updated. - content: - application/json: - schema: - $ref: '#/components/schemas/Quote' - '400': - description: Bad request - Invalid quote ID or quote cannot be confirmed - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Quote already confirmed, expired, or in invalid state - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions: - get: - summary: List transactions - description: | - Retrieve a paginated list of transactions with optional filtering. - The transactions can be filtered by customer ID, platform customer ID, UMA address, - date range, status, and transaction type. - operationId: listTransactions - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: customerId - in: query - description: Filter by system customer ID - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: Filter by platform-specific customer ID - required: false - schema: - type: string - - name: senderAccountIdentifier - in: query - description: Filter by sender account identifier - required: false - schema: - type: string - - name: receiverAccountIdentifier - in: query - description: Filter by receiver account identifier - required: false - schema: - type: string - - name: status - in: query - description: Filter by transaction status - required: false - schema: - $ref: '#/components/schemas/TransactionStatus' - - name: type - in: query - description: Filter by transaction type - required: false - schema: - $ref: '#/components/schemas/TransactionType' - - name: reference - in: query - description: Filter by reference - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of transactions matching the criteria - items: - $ref: '#/components/schemas/Transaction' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of transactions matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions/{transactionId}: - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction - required: true - schema: - type: string - get: - summary: Get transaction by ID - description: Retrieve detailed information about a specific transaction. - operationId: getTransactionById - tags: - - Transactions - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/Transaction' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions/{transactionId}/approve: - post: - summary: Approve a pending incoming payment - description: | - Approve a pending incoming payment that was previously acknowledged with a 202 response. - This endpoint allows platforms to asynchronously approve payments after async processing. - operationId: approvePendingPayment - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction to approve - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - receiverCustomerInfo: - type: object - additionalProperties: true - description: Information about the recipient, provided by the platform if requested in the original webhook via `requestedReceiverCustomerInfoFields`. - responses: - '200': - description: Payment approved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingTransaction' - '400': - description: Bad request - Invalid parameters or payment cannot be approved - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Payment is not in a pending state or has already been processed or timed out. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /transactions/{transactionId}/reject: - post: - summary: Reject a pending incoming payment - description: | - Reject a pending incoming payment that was previously acknowledged with a 202 response. - This endpoint allows platforms to asynchronously reject payments after additional processing. - operationId: rejectPendingPayment - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction to reject - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - reason: - type: string - description: Optional reason for rejecting the payment. This is just for debugging purposes or can be used for a platform's own purposes. - example: RESTRICTED_JURISDICTION - responses: - '200': - description: Payment rejected successfully - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingTransaction' - '400': - description: Bad request - Invalid parameters or payment cannot be rejected - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict - Payment is not in a pending state or has already been processed or timed out. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /webhooks/test: - post: - summary: Send a test webhook - description: Send a test webhook to the configured endpoint - operationId: sendTestWebhook - tags: - - Webhooks - security: - - BasicAuth: [] - responses: - '200': - description: Webhook delivered successfully - content: - application/json: - schema: - $ref: '#/components/schemas/TestWebhookResponse' - '400': - description: Bad request - Webhook delivery failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/bulk/csv: - post: - summary: Upload customers via CSV file - description: | - Upload a CSV file containing customer information for bulk creation. The CSV file should follow - a specific format with required and optional columns based on customer type. - - ### CSV Format - The CSV file should have the following columns: - - Required columns for all customers: - - umaAddress: The customer's UMA address (e.g., $john.doe@uma.domain.com) - - platformCustomerId: Your platform's unique identifier for the customer - - customerType: Either "INDIVIDUAL" or "BUSINESS" - - Required columns for individual customers: - - fullName: Individual's full name - - birthDate: Date of birth in YYYY-MM-DD format - - addressLine1: Street address line 1 - - city: City - - state: State/Province/Region - - postalCode: Postal/ZIP code - - country: Country code (ISO 3166-1 alpha-2) - - Required columns for business customers: - - businessLegalName: Legal name of the business - - addressLine1: Street address line 1 - - city: City - - state: State/Province/Region - - postalCode: Postal/ZIP code - - country: Country code (ISO 3166-1 alpha-2) - - Optional columns for all customers: - - addressLine2: Street address line 2 - - platformAccountId: Your platform's identifier for the bank account - - description: Optional description for the customer - - Optional columns for individual customers: - - email: Customer's email address - - Optional columns for business customers: - - businessRegistrationNumber: Business registration number - - businessTaxId: Tax identification number - - ### Example CSV - ```csv - umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName - john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main St,San Francisco,CA,94105,US - acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce Way,Austin,TX,78701,US - ``` - - The upload process is asynchronous and will return a job ID that can be used to track progress. - You can monitor the job status using the `/customers/bulk/jobs/{jobId}` endpoint. - operationId: uploadCustomersCsv - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - file - properties: - file: - type: string - format: binary - description: CSV file containing customer information - responses: - '202': - description: CSV upload accepted for processing - content: - application/json: - schema: - type: object - required: - - jobId - - status - properties: - jobId: - type: string - description: Unique identifier for the bulk import job - example: Job:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /customers/bulk/jobs/{jobId}: - get: - summary: Get bulk import job status - description: | - Retrieve the current status and results of a bulk customer import job. This endpoint can be used - to track the progress of both CSV uploads. - - The response includes: - - Overall job status - - Progress statistics - - Detailed error information for failed entries - - Completion timestamp when finished - operationId: getBulkCustomerImportJob - tags: - - Customers - security: - - BasicAuth: [] - parameters: - - name: jobId - in: path - description: ID of the bulk import job to retrieve - required: true - schema: - type: string - responses: - '200': - description: Job status retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/BulkCustomerImportJob' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Job not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations: - post: - summary: Create an UMA invitation - description: | - Create an UMA invitation from a given platform customer. - operationId: createInvitation - tags: - - Invitations - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - inviterUma - properties: - inviterUma: - type: string - description: The UMA address of the customer creating the invitation - example: $inviter@uma.domain - firstName: - type: string - description: First name of the invitee to show as part of the invite - example: Alice - amountToSend: - description: | - An amount to send (in the smallest unit of the customer's currency) to the invitee when the invitation is claimed. - This is optional and if not provided, the invitee will not receive any amount. Note that the actual sending of - the amount must be done by the inviter platform once the INVITATION_CLAIMED webhook is received. If the inviter - platform either does not send the payment or the payment fails, the invitee will not receive this amount. This - field is primarily used for display purposes on the claiming side of the invitation. - type: integer - format: int64 - example: 12550 - expiresAt: - type: string - format: date-time - description: When the invitation expires (if at all) - example: '2025-09-01T14:30:00Z' - responses: - '201': - description: Invitation created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations/{invitationCode}: - get: - summary: Get an UMA invitation by code - description: | - Retrieve details about an UMA invitation by its invitation code. - operationId: getInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to get - required: true - schema: - type: string - responses: - '200': - description: Invitation retrieved successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations/{invitationCode}/claim: - post: - summary: Claim an UMA invitation - description: | - Claim an UMA invitation by associating it with an invitee UMA address. - - When an invitation is successfully claimed: - 1. The invitation status changes from PENDING to CLAIMED - 2. The invitee UMA address is associated with the invitation - 3. An INVITATION_CLAIMED webhook is triggered to notify the platform that created the invitation - - This endpoint allows customers to accept invitations sent to them by other UMA customers. - operationId: claimInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to claim - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - inviteeUma - properties: - inviteeUma: - type: string - description: The UMA address of the customer claiming the invitation - example: $invitee@uma.domain - responses: - '200': - description: Invitation claimed successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /invitations/{invitationCode}/cancel: - post: - summary: Cancel an UMA invitation - description: | - Cancel a pending UMA invitation. Only the inviter or platform can cancel an invitation. - - When an invitation is cancelled: - 1. The invitation status changes from PENDING to CANCELLED - 2. The invitation can no longer be claimed - 3. The invitation URL will show as cancelled when accessed - - Only pending invitations can be cancelled. Attempting to cancel an invitation - that is already claimed, expired, or cancelled will result in an error. - operationId: cancelInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to cancel - required: true - schema: - type: string - responses: - '200': - description: Invitation cancelled successfully - content: - application/json: - schema: - $ref: '#/components/schemas/UmaInvitation' - '400': - description: Bad request - Invitation cannot be cancelled (already claimed, expired, or cancelled) - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - Only the platform which created the invitation can cancel it - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /sandbox/send: - post: - summary: Simulate sending funds - description: | - Simulate sending funds to the bank account as instructed in the quote. - This endpoint is only for the sandbox environment and will fail for production platforms/keys. - operationId: sandboxSend - tags: - - Sandbox - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - quoteId - - currencyCode - properties: - quoteId: - type: string - description: The unique identifier of the quote - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - currencyCode: - type: string - description: Currency code for the funds to be sent - example: USD - currencyAmount: - type: integer - format: int64 - description: The amount to send in the smallest unit of the currency (eg. cents). If not provided, the amount will be derived from the quote. - exclusiveMinimum: 0 - example: 1000 - responses: - '200': - description: Funds received successfully - content: - application/json: - schema: - $ref: '#/components/schemas/OutgoingTransaction' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Quote not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /sandbox/uma/receive: - post: - summary: Simulate payment send to test receiving an UMA payment - description: | - Simulate sending payment from an sandbox uma address to a platform customer to test payment receive. - This endpoint is only for the sandbox environment and will fail for production platforms/keys. - operationId: sandboxReceive - tags: - - Sandbox - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - senderUmaAddress - - receivingCurrencyCode - - receivingCurrencyAmount - properties: - senderUmaAddress: - type: string - description: UMA address of the sender from the sandbox - example: $success.usd@sandbox.grid.uma.money - receiverUmaAddress: - type: string - description: UMA address of the receiver (optional if customerId is provided) - example: $receiver@uma.domain - customerId: - type: string - description: System ID of the receiver (optional if receiverUmaAddress is provided) - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - receivingCurrencyCode: - type: string - description: The currency code for the receiving amount - example: USD - receivingCurrencyAmount: - type: integer - format: int64 - description: The amount to be received in the smallest unit of the currency (eg. cents) - exclusiveMinimum: 0 - example: 1000 - responses: - '200': - description: Payment triggered successfully - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingTransaction' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Sender or receiver not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /sandbox/internal-accounts/{accountId}/fund: - post: - summary: Simulate funding an internal account - description: | - Simulate receiving funds into an internal account in the sandbox environment. This is useful for testing scenarios where you need to add funds to a customer's or platform's internal account without going through a real bank transfer or following payment instructions. - This endpoint is only for the sandbox environment and will fail for production platforms/keys. - operationId: sandboxFundInternalAccount - tags: - - Sandbox - security: - - BasicAuth: [] - parameters: - - name: accountId - in: path - required: true - description: The ID of the internal account to fund - schema: - type: string - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - amount - properties: - amount: - type: integer - format: int64 - description: Amount to add in the smallest unit of the account's currency (e.g., cents for USD/EUR, satoshis for BTC) - exclusiveMinimum: 0 - maximum: 100000000000 - example: 100000 - examples: - fundUSDAccount: - summary: Fund USD internal account with $1,000 - value: - amount: 100000 - fundBTCAccount: - summary: Fund BTC internal account with 0.01 BTC - value: - amount: 1000000 - responses: - '200': - description: Internal account funded successfully - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccount' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: '#/components/schemas/Error403' - '404': - description: Internal account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /uma-providers: - get: - summary: List available Counterparty Providers - description: | - Retrieve a list of available Counterparty Providers. The response includes basic information about each provider, such as its UMA address, name, and supported currencies. - operationId: getAvailableUmaProviders - tags: - - Available UMA Providers - parameters: - - in: query - name: countryCode - description: The alpha-2 representation of a country, as defined by the ISO 3166-1 standard. - required: false - schema: - type: string - example: US - - in: query - name: currencyCode - description: The ISO 4217 currency code to filter providers by supported currency. - required: false - schema: - type: string - example: USD - - in: query - name: hasBlockedProviders - description: Whether to include providers which are not on your allowlist in the response. By default the response will include blocked providers. - required: false - schema: - type: boolean - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - properties: - data: - description: List of available UMA Providers using Grid - type: array - items: - $ref: '#/components/schemas/UmaProvider' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of transactions matching the criteria (excluding pagination) - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /tokens: - post: - summary: Create a new API token - description: Create a new API token to access the Grid APIs. - operationId: createToken - tags: - - API Tokens - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - title: tokenCreate - properties: - name: - type: string - description: Name of the token to help identify it - example: Sandbox read-only - permissions: - type: array - description: A list of permissions to grant to the token - items: - $ref: '#/components/schemas/Permission' - required: - - name - - permissions - responses: - '201': - description: API token created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/ApiToken' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - get: - summary: List tokens - description: | - Retrieve a list of API tokens with optional filtering parameters. Returns all tokens that match - the specified filters. If no filters are provided, returns all tokens (paginated). - operationId: listTokens - tags: - - API Tokens - security: - - BasicAuth: [] - parameters: - - name: name - in: query - description: Filter by name of the token - required: false - schema: - type: string - - name: createdAfter - in: query - description: Filter customers created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter customers created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedAfter - in: query - description: Filter customers updated after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedBefore - in: query - description: Filter customers updated before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of tokens matching the filter criteria - items: - $ref: '#/components/schemas/ApiToken' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of tokens matching the criteria (excluding pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - /tokens/{tokenId}: - parameters: - - name: tokenId - in: path - description: System-generated unique token identifier - required: true - schema: - type: string - get: - summary: Get API token by ID - description: Retrieve an API token by their system-generated ID - operationId: getTokenById - tags: - - API Tokens - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/ApiToken' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Token not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' - delete: - summary: Delete API token by ID - description: Delete an API token by their system-generated ID - operationId: deleteTokenById - tags: - - API Tokens - security: - - BasicAuth: [] - responses: - '204': - description: API token deleted successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Token not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' -webhooks: - incoming-payment: - post: - summary: Incoming payment webhook and approval mechanism - description: | - Webhook that is called when an incoming payment is received by a customer's UMA address. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - ### Payment Approval Flow - When a transaction has `status: "PENDING"`, this webhook serves as an approval mechanism: - - 1. The client should check the `counterpartyInformation` against their requirements - 2. To APPROVE the payment synchronously, return a 200 OK response - 3. To REJECT the payment, return a 403 Forbidden response with an Error object - 4. To request more information, return a 422 Unprocessable Entity with specific missing fields - 5. To process the payment asynchronously, return a 202 Accepted response and then call the `/transactions/{transactionId}/approve` or `/transactions/{transactionId}/reject` endpoint within 5 seconds. Note that synchronous approval/rejection is preferred where possible. - - The Grid system will proceed or cancel the payment based on your response. - - For transactions with other statuses (COMPLETED, FAILED, REFUNDED), this webhook is purely informational. - operationId: incomingPaymentWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhook' - examples: - pendingPayment: - summary: Pending payment example requiring approval - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: PENDING - type: INCOMING - senderUmaAddress: $sender@external.domain - receiverUmaAddress: $recipient@uma.domain - receivedAmount: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - reconciliationInstructions: - reference: REF-123456789 - counterpartyInformation: - FULL_NAME: John Sender - BIRTH_DATE: '1985-06-15' - NATIONALITY: US - requestedReceiverCustomerInfoFields: - - name: NATIONALITY - mandatory: true - - name: ADDRESS - mandatory: false - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: INCOMING_PAYMENT - incomingCompletedPayment: - summary: Completed payment notification - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: COMPLETED - type: INCOMING - senderUmaAddress: $sender@external.domain - receiverUmaAddress: $recipient@uma.domain - receivedAmount: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - settledAt: '2025-08-15T14:30:00Z' - createdAt: '2025-08-15T14:25:18Z' - description: Payment for services - reconciliationInstructions: - reference: REF-123456789 - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: INCOMING_PAYMENT - responses: - '200': - description: | - Webhook received successfully. - For PENDING transactions, this indicates approval to proceed with the payment. - If `requestedReceiverCustomerInfoFields` were present in the webhook request, the corresponding fields for the recipient must be included in this response in the `receiverCustomerInfo` object. - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhookResponse' - '202': - description: | - Webhook received and will be processed asynchronously. The synchronous 200 response should be preferred where possible. This asycnhronous path should only be used in - cases where the platform's architecture requires async (but still very quick) processing before approving or rejecting the payment. - The platform must call the `/transactions/{transactionId}/approve` or `/transactions/{transactionId}/reject` endpoint to approve or reject the payment within 5 seconds or the payment will be automatically rejected. - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '403': - description: | - Forbidden - Payment rejected by the client. - Only applicable for PENDING transactions. - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhookForbiddenResponse' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '422': - description: | - Unprocessable Entity - Additional counterparty information required. - Only applicable for PENDING transactions. - content: - application/json: - schema: - $ref: '#/components/schemas/IncomingPaymentWebhookUnprocessableResponse' - outgoing-payment: - post: - summary: Outgoing payment status webhook - description: | - Webhook that is called when an outgoing payment's status changes. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - This webhook is informational only and is sent when an outgoing payment completes successfully or fails. - operationId: outgoingPaymentWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/OutgoingPaymentWebhook' - examples: - outgoingCompletedPayment: - summary: Completed outgoing payment - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: COMPLETED - type: OUTGOING - senderUmaAddress: $sender@uma.domain - receiverUmaAddress: $recipient@external.domain - sentAmount: - amount: 10550 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - receivedAmount: - amount: 9706 - currency: - code: EUR - name: Euro - symbol: € - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - settlementTime: '2025-08-15T14:30:00Z' - createdAt: '2025-08-15T14:25:18Z' - description: 'Payment for invoice #1234' - exchangeRate: 0.92 - quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006 - paymentInstructions: - - accountOrWalletInfo: - reference: UMA-Q12345-REF - accountType: US_ACCOUNT - accountNumber: 987654321 - routingNumber: 123456789 - accountCategory: CHECKING - bankName: Chase Bank - - accountOrWalletInfo: - accountType: SOLANA_WALLET - assetType: USDC - address: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: OUTGOING_PAYMENT - failedPayment: - summary: Failed outgoing payment - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: FAILED - type: OUTGOING - senderUmaAddress: $sender@uma.domain - receiverUmaAddress: $recipient@external.domain - sentAmount: - amount: 10550 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - createdAt: '2025-08-15T14:25:18Z' - quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006 - paymentInstructions: - - accountOrWalletInfo: - reference: UMA-Q12345-REF - accountType: US_ACCOUNT - accountNumber: 987654321 - routingNumber: 123456789 - accountCategory: CHECKING - bankName: Chase Bank - - accountOrWalletInfo: - accountType: SOLANA_WALLET - assetType: USDC - address: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: OUTGOING_PAYMENT - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - test-webhook: - post: - summary: Test webhook for integration verification - description: | - Webhook that is sent once to verify your webhook endpoint is correctly set up. - This is sent when you configure or update your platform settings with a webhook URL. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by the Grid API. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - This webhook is purely for testing your endpoint integration and signature verification. - operationId: testWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TestWebhookRequest' - examples: - testWebhook: - summary: Test webhook example - value: - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000001 - type: TEST - responses: - '200': - description: Webhook received successfully. This confirms your webhook endpoint is properly configured. - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - bulk-upload: - post: - summary: Bulk upload status webhook - description: | - Webhook that is called when a bulk customer upload job completes or fails. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - This webhook is sent when a bulk upload job completes or fails, providing detailed information about the results. - operationId: bulkUploadWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BulkUploadWebhookRequest' - examples: - completedUpload: - summary: Successful bulk upload completion - value: - bulkCustomerImportJob: - jobId: Job:019542f5-b3e7-1d02-0000-000000000006 - status: COMPLETED - progress: - total: 5000 - processed: 5000 - successful: 5000 - failed: 0 - errors: [] - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: BULK_UPLOAD - timestamp: '2025-08-15T14:32:00Z' - failedUpload: - summary: Failed bulk upload - value: - bulkCustomerImportJob: - jobId: Job:019542f5-b3e7-1d02-0000-000000000006 - status: FAILED - progress: - total: 5000 - processed: 5000 - successful: 0 - failed: 5000 - errors: - - correlationId: row_1 - error: - code: invalid_csv_format - message: Invalid CSV format - details: - reason: missing_required_column - column: umaAddress - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: BULK_UPLOAD - timestamp: '2025-08-15T14:32:00Z' - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - invitation-claimed: - post: - summary: Invitation claimed webhook - description: | - Webhook that is called when an invitation is claimed by a customer. - This endpoint should be implemented by platform clients of the Grid API. - - When a customer claims an invitation, this webhook is triggered to notify the platform that: - 1. The invitation has been successfully claimed - 2. The invitee UMA address is now associated with the invitation - 3. The invitation status has changed from PENDING to CLAIMED - - This allows platforms to: - - Track invitation usage and conversion rates - - Trigger onboarding flows for new customers who joined via invitation - - Apply referral bonuses or rewards to the inviter - - Update their UI to reflect the claimed status - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - operationId: invitationClaimedWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/InvitationClaimedWebhook' - examples: - claimedInvitation: - summary: Invitation claimed notification - value: - invitation: - code: 019542f5 - createdAt: '2025-09-01T14:30:00Z' - claimedAt: '2025-09-01T15:45:00Z' - inviterUma: $inviter@uma.domain - inviteeUma: $invitee@uma.domain - status: CLAIMED - url: https://uma.me/i/019542f5 - timestamp: '2025-09-01T15:45:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: INVITATION_CLAIMED - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - kyc-status: - post: - summary: KYC customer status change - description: | - Webhook that is called when the KYC status of a customer is updated. - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid API public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - ### KYC/B Flow - This webhook is triggered when KYC/B has reached a decision on a customer. Generally most customers will finish KYC within a few minutes. Others might be rejected because of incorrect data passed in or may have been flagged for manual review. - The webhook will only trigger for final states. This will be APPROVED, REJECTED, EXPIRED, CANCELED, MANUALLY_APPROVED, MANUALLY_REJECTED. - - * APPROVED: The customer has been approved. - * REJECTED: The customer has been rejected after a KYC check. - * PENDING_REVIEW: KYC check is in progress. - * EXPIRED: KYC check has expired. This is generally because a customer did not submit all required information needed within a session. - * CANCELED: KYC check was canceled. - * MANUALLY_APPROVED: The customer was manually approved. - * MANUALLY_REJECTED: The customer was manually rejected. - * NOT_STARTED: KYC has not started on the customer. - operationId: kycStatusWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/KycStatusWebhook' - examples: - kycApprovedWebhook: - summary: When a customer KYC has been approved - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - kycStatus: APPROVED - type: KYC_STATUS - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - responses: - '200': - description: | - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - account-status: - post: - summary: Account status notification webhook - description: | - Webhook that is called when the balance of an account changes - This endpoint should be implemented by clients of the Grid API. - - ### Authentication - The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - - ### Account status - When the balance of an internal account changes, we will push a notification with information on the account, the new balance, and who the account belongs to. - operationId: accountStatusWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AccountStatusWebhook' - examples: - balanceDecrease: - summary: A transaction just cleared a customer account and the balance has decreased - value: - account: - accountId: Account:019542f5-b3e7-1d02-0000-000000000005 - oldBalance: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - newBalance: - amount: 10000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 019542f5-b3e7-1d02-0000-000000000001 - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: ACCOUNT_STATUS - responses: - '200': - description: | - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' -components: - securitySchemes: - BasicAuth: - type: http - scheme: basic - description: API token authentication using format `:` - WebhookSignature: - type: apiKey - in: header - name: X-Grid-Signature - description: | - Secp256r1 (P-256) asymmetric signature of the webhook payload, which can be used to verify that the webhook was sent by Grid. - - To verify the signature: - 1. Get the Grid public key provided to you during integration - 2. Decode the base64 signature from the header - 3. Create a SHA-256 hash of the request body - 4. Verify the signature using the public key and the hash - - If the signature verification succeeds, the webhook is authentic. If not, it should be rejected. - schemas: - AllErrors: - anyOf: - - $ref: '#/components/schemas/Error400' - - $ref: '#/components/schemas/Error401' - - $ref: '#/components/schemas/Error403' - - $ref: '#/components/schemas/Error404' - - $ref: '#/components/schemas/Error409' - - $ref: '#/components/schemas/Error410' - - $ref: '#/components/schemas/Error412' - - $ref: '#/components/schemas/Error424' - - $ref: '#/components/schemas/Error500' - - $ref: '#/components/schemas/Error501' - CustomerInfoFieldName: - type: string - enum: - - FULL_NAME - - BIRTH_DATE - - NATIONALITY - - PHONE_NUMBER - - EMAIL - - POSTAL_ADDRESS - - TAX_ID - - REGISTRATION_NUMBER - - USER_TYPE - - COUNTRY_OF_RESIDENCE - - ACCOUNT_IDENTIFIER - - FI_LEGAL_ENTITY_NAME - - FI_ADDRESS - - PURPOSE_OF_PAYMENT - - ULTIMATE_INSTITUTION_COUNTRY - - IDENTIFIER - description: Name of a type of field containing info about a platform's customer or counterparty customer. - example: FULL_NAME - CounterpartyFieldDefinition: - type: object - properties: - name: - $ref: '#/components/schemas/CustomerInfoFieldName' - mandatory: - type: boolean - description: Whether the field is mandatory - example: true - required: - - name - - mandatory - TransactionType: - type: string - enum: - - INCOMING - - OUTGOING - description: Type of transaction (incoming payment or outgoing payment) - PlatformCurrencyConfig: - type: object - properties: - currencyCode: - type: string - description: Three-letter currency code (ISO 4217) - example: USD - minAmount: - type: integer - format: int64 - description: Minimum amount that can be sent in the smallest unit of this currency - minimum: 0 - example: 100 - maxAmount: - type: integer - format: int64 - description: Maximum amount that can be sent in the smallest unit of this currency - minimum: 0 - example: 1000000 - requiredCounterpartyFields: - type: array - items: - $ref: '#/components/schemas/CounterpartyFieldDefinition' - description: List of fields which the platform requires from the counterparty institutions about counterparty customers. Platforms can set mandatory to false if the platform does not require the field, but would like to have it available. Some fields may be required by the underlying UMA provider. - example: - - name: FULL_NAME - mandatory: true - - name: BIRTH_DATE - mandatory: true - - name: NATIONALITY - mandatory: true - providerRequiredCustomerFields: - type: array - items: - $ref: '#/components/schemas/CustomerInfoFieldName' - description: List of customer info field names that are required by the underlying UMA provider when creating a customer for this currency. These fields must be supplied when creating or updating a customer if this currency is intended to be used by that customer. If no fields are required, this field is omitted. - readOnly: true - example: - - NATIONALITY - - BIRTH_DATE - providerRequiredCounterpartyCustomerFields: - type: array - items: - $ref: '#/components/schemas/CustomerInfoFieldName' - description: List of fields that are required by the underlying UMA provider for this currency. If the counterparty does not provide these fields, quote requests will fail. - readOnly: true - example: - - FULL_NAME - - COUNTRY_OF_RESIDENCE - enabledTransactionTypes: - type: array - items: - $ref: '#/components/schemas/TransactionType' - description: List of transaction types that are enabled for this currency. - example: - - OUTGOING - - INCOMING - required: - - currencyCode - - minAmount - - maxAmount - - requiredCounterpartyFields - - enabledTransactionTypes - PlatformConfig: - type: object - properties: - id: - type: string - description: System-generated unique identifier - readOnly: true - example: PlatformConfig:019542f5-b3e7-1d02-0000-000000000003 - umaDomain: - type: string - description: UMA domain for this platform - example: platform.uma.domain - proxyUmaSubdomain: - type: string - description: The subdomain that incoming requests will be proxied to - example: platform - webhookEndpoint: - type: string - description: URL where webhook notifications will be sent - example: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - type: array - items: - $ref: '#/components/schemas/PlatformCurrencyConfig' - description: | - List of currencies supported by the platform. This is what the platform's - customers are able to hold, send, and receive. - isRegulatedFinancialInstitution: - type: boolean - description: | - Whether the platform is a regulated financial institution. This is used to - determine if the platform's customers must be KYC/KYB'd by Lightspark via - the KYC link flow. This can only be set by Lightspark during platform - creation. - example: false - createdAt: - type: string - format: date-time - description: Creation timestamp - readOnly: true - example: '2025-06-15T12:30:45Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - readOnly: true - example: '2025-06-15T12:30:45Z' - Error401: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 401 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNAUTHORIZED | Issue with API credentials | - | INVALID_SIGNATURE | Signature header is invalid | - enum: - - UNAUTHORIZED - - INVALID_SIGNATURE - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error500: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 500 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | GRID_SWITCH_ERROR | Grid switch error | - | INTERNAL_ERROR | Internal server or UMA error | - enum: - - GRID_SWITCH_ERROR - - INTERNAL_ERROR - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error400: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 400 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | INVALID_INPUT | Invalid input provided | - | MISSING_MANDATORY_USER_INFO | Required customer information is missing | - | INVITATION_ALREADY_CLAIMED | Invitation has already been claimed | - | INVITATIONS_NOT_CONFIGURED | Invitations are not configured | - | INVALID_UMA_ADDRESS | UMA address format is invalid | - | INVITATION_CANCELLED | Invitation has been cancelled | - | QUOTE_REQUEST_FAILED | An issue occurred during the quote process; this is retryable | - | INVALID_PAYREQ_RESPONSE | Counterparty Payreq response was invalid | - | INVALID_RECEIVER | Receiver is invalid | - | PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq response | - | CERT_CHAIN_INVALID | Counterparty certificate chain is invalid | - | CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired | - | INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid | - | MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA parameters are missing | - | SENDER_NOT_ACCEPTED | Sender is not accepted | - | AMOUNT_OUT_OF_RANGE | Amount is out of range | - | INVALID_CURRENCY | Currency is invalid | - | INVALID_TIMESTAMP | Timestamp is invalid | - | INVALID_NONCE | Nonce is invalid | - | INVALID_REQUEST_FORMAT | Request format is invalid | - | INVALID_BANK_ACCOUNT | Bank account is invalid | - | SELF_PAYMENT | Self payment not allowed | - | LOOKUP_REQUEST_FAILED | Lookup request failed | - | PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response | - | INVALID_AMOUNT | Amount is invalid | - | WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set | - | WEBHOOK_DELIVERY_ERROR | Webhook delivery error | - enum: - - INVALID_INPUT - - MISSING_MANDATORY_USER_INFO - - INVITATION_ALREADY_CLAIMED - - INVITATIONS_NOT_CONFIGURED - - INVALID_UMA_ADDRESS - - INVITATION_CANCELLED - - QUOTE_REQUEST_FAILED - - INVALID_PAYREQ_RESPONSE - - INVALID_RECEIVER - - PARSE_PAYREQ_RESPONSE_ERROR - - CERT_CHAIN_INVALID - - CERT_CHAIN_EXPIRED - - INVALID_PUBKEY_FORMAT - - MISSING_REQUIRED_UMA_PARAMETERS - - SENDER_NOT_ACCEPTED - - AMOUNT_OUT_OF_RANGE - - INVALID_CURRENCY - - INVALID_TIMESTAMP - - INVALID_NONCE - - INVALID_REQUEST_FORMAT - - INVALID_BANK_ACCOUNT - - SELF_PAYMENT - - LOOKUP_REQUEST_FAILED - - PARSE_LNURLP_RESPONSE_ERROR - - INVALID_AMOUNT - - WEBHOOK_ENDPOINT_NOT_SET - - WEBHOOK_DELIVERY_ERROR - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error501: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 501 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY | Unrecognized mandatory payee data key | - | NOT_IMPLEMENTED | Feature not implemented | - enum: - - UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY - - NOT_IMPLEMENTED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - CustomerType: - type: string - enum: - - INDIVIDUAL - - BUSINESS - description: Whether the customer is an individual or a business entity - example: INDIVIDUAL - IndividualCustomer: - allOf: - - $ref: '#/components/schemas/Customer' - - $ref: '#/components/schemas/IndividualCustomerFields' - Customer: - type: object - required: - - umaAddress - - platformCustomerId - - customerType - properties: - id: - type: string - description: System-generated unique identifier - readOnly: true - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: Platform-specific customer identifier - example: 9f84e0c2a72c4fa - customerType: - $ref: '#/components/schemas/CustomerType' - kycStatus: - $ref: '#/components/schemas/KycStatus' - umaAddress: - type: string - description: Full UMA address (always present in responses, even if system-generated). This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com - createdAt: - type: string - format: date-time - description: Creation timestamp - readOnly: true - example: '2025-07-21T17:32:28Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - readOnly: true - example: '2025-07-21T17:32:28Z' - isDeleted: - type: boolean - description: Whether the customer is marked as deleted - example: false - readOnly: true - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - Address: - type: object - required: - - line1 - - postalCode - - country - properties: - line1: - type: string - description: Street address line 1 - example: 123 Main Street - line2: - type: string - description: Street address line 2 - example: Apt 4B - city: - type: string - description: City - example: San Francisco - state: - type: string - description: State/Province/Region - example: CA - postalCode: - type: string - description: Postal/ZIP code - example: '94105' - country: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - BusinessInfoUpdate: - type: object - description: Additional information for business entities - properties: - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 - UltimateBeneficialOwner: - type: object - required: - - fullName - - individualType - properties: - fullName: - type: string - description: Individual's full name - example: John Michael Doe - emailAddress: - type: string - format: email - description: Email address of the individual - example: example@test.com - phoneNumber: - type: string - description: Phone number of the individual in E.164 format - example: '+5555555555' - pattern: ^\+[1-9]\d{1,14}$ - taxId: - type: string - description: Tax identification number of the individual. This could be a Social Security Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or a Passport Number. - example: EIN-987654321 - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - address: - $ref: '#/components/schemas/Address' - individualType: - type: string - enum: - - DIRECTOR - - CONTROL_PERSON - - BUSINESS_POINT_OF_CONTACT - - TRUSTEE - - SETTLOR - - GENERAL_PARTNER - description: Type of individual in the corporation - example: DIRECTOR - percentageOwnership: - type: number - description: Percent of ownership when individual type is beneficial owner - example: 25 - title: - type: string - description: Title at company - example: CEO, COO, President - BusinessCustomerFields: - type: object - required: - - customerType - properties: - customerType: - type: string - enum: - - BUSINESS - address: - $ref: '#/components/schemas/Address' - businessInfo: - $ref: '#/components/schemas/BusinessInfoUpdate' - beneficialOwners: - type: array - items: - $ref: '#/components/schemas/UltimateBeneficialOwner' - BusinessInfo: - type: object - description: Additional information required for business entities - required: - - legalName - properties: - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 - BusinessCustomer: - allOf: - - $ref: '#/components/schemas/Customer' - - $ref: '#/components/schemas/BusinessCustomerFields' - - type: object - properties: - businessInfo: - $ref: '#/components/schemas/BusinessInfo' - KycStatus: - type: string - enum: - - APPROVED - - REJECTED - - PENDING_REVIEW - - EXPIRED - - CANCELED - - MANUALLY_APPROVED - - MANUALLY_REJECTED - description: The current KYC status of a customer - example: APPROVED - IndividualCustomerFields: - type: object - required: - - customerType - properties: - customerType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: Individual's full name - example: John Michael Doe - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - address: - $ref: '#/components/schemas/Address' - CustomerOneOf: - oneOf: - - title: Individual Customer - $ref: '#/components/schemas/IndividualCustomer' - - title: Business Customer - $ref: '#/components/schemas/BusinessCustomer' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' - IndividualCustomerCreateRequest: - allOf: - - $ref: '#/components/schemas/CustomerCreateRequest' - - $ref: '#/components/schemas/IndividualCustomerFields' - CustomerCreateRequest: - type: object - required: - - customerType - - platformCustomerId - properties: - platformCustomerId: - type: string - description: Platform-specific customer identifier. If not provided, one will be generated by the system. - example: 9f84e0c2a72c4fa - customerType: - $ref: '#/components/schemas/CustomerType' - umaAddress: - type: string - description: Optional UMA address identifier. If not provided during customer creation, one will be generated by the system. If provided during customer update, the UMA address will be updated to the provided value. This is an optional identifier to route payments to the customer. This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerCreateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerCreateRequest' - BusinessCustomerCreateRequest: - allOf: - - $ref: '#/components/schemas/CustomerCreateRequest' - - $ref: '#/components/schemas/BusinessCustomerFields' - - type: object - properties: - businessInfo: - $ref: '#/components/schemas/BusinessInfo' - CustomerCreateRequestOneOf: - oneOf: - - title: Individual Customer Create Request - $ref: '#/components/schemas/IndividualCustomerCreateRequest' - - title: Business Customer Create Request - $ref: '#/components/schemas/BusinessCustomerCreateRequest' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerCreateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerCreateRequest' - Error409: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 409 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | - | UMA_ADDRESS_EXISTS | UMA address already exists | - enum: - - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - - UMA_ADDRESS_EXISTS - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error404: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 404 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | TRANSACTION_NOT_FOUND | Transaction not found | - | INVITATION_NOT_FOUND | Invitation not found | - | USER_NOT_FOUND | Customer not found | - | QUOTE_NOT_FOUND | Quote not found | - | LOOKUP_REQUEST_NOT_FOUND | Lookup request not found | - | TOKEN_NOT_FOUND | Token not found | - | BULK_UPLOAD_JOB_NOT_FOUND | Bulk upload job not found | - | REFERENCE_NOT_FOUND | Reference not found | - enum: - - TRANSACTION_NOT_FOUND - - INVITATION_NOT_FOUND - - USER_NOT_FOUND - - QUOTE_NOT_FOUND - - LOOKUP_REQUEST_NOT_FOUND - - TOKEN_NOT_FOUND - - BULK_UPLOAD_JOB_NOT_FOUND - - REFERENCE_NOT_FOUND - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error410: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 410 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | CUSTOMER_DELETED | Customer has been permanently deleted | - enum: - - CUSTOMER_DELETED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - IndividualCustomerUpdateRequest: - allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/IndividualCustomerFields' - CustomerUpdateRequest: - type: object - required: - - customerType - properties: - customerType: - $ref: '#/components/schemas/CustomerType' - umaAddress: - type: string - description: Optional UMA address identifier. If provided, the customer's UMA address will be updated. This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' - BusinessCustomerUpdateRequest: - allOf: - - $ref: '#/components/schemas/CustomerUpdateRequest' - - $ref: '#/components/schemas/BusinessCustomerFields' - CustomerUpdateRequestOneOf: - oneOf: - - title: Individual Customer Update Request - $ref: '#/components/schemas/IndividualCustomerUpdateRequest' - - title: Business Customer Update Request - $ref: '#/components/schemas/BusinessCustomerUpdateRequest' - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomerUpdateRequest' - BUSINESS: '#/components/schemas/BusinessCustomerUpdateRequest' - Currency: - type: object - properties: - code: - type: string - description: Three-letter currency code (ISO 4217) for fiat currencies. Some cryptocurrencies may use their own ticker symbols (e.g. "BTC" for Bitcoin, "USDC" for USDC, etc.) - example: USD - name: - type: string - description: Full name of the currency - example: United States Dollar - symbol: - type: string - description: Symbol of the currency - example: $ - decimals: - type: integer - description: Number of decimal places for the currency - minimum: 0 - example: 2 - CurrencyAmount: - type: object - required: - - amount - - currency - properties: - amount: - type: integer - format: int64 - description: Amount in the smallest unit of the currency (e.g., cents for USD/EUR, satoshis for BTC) - example: 12550 - currency: - $ref: '#/components/schemas/Currency' - PaymentClabeAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/ClabeAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - BasePaymentAccountInfo: - type: object - required: - - accountType - properties: - accountType: - $ref: '#/components/schemas/PaymentAccountType' - discriminator: - propertyName: accountType - mapping: - CLABE: '#/components/schemas/PaymentClabeAccountInfo' - US_ACCOUNT: '#/components/schemas/PaymentUsAccountInfo' - PIX: '#/components/schemas/PaymentPixAccountInfo' - IBAN: '#/components/schemas/PaymentIbanAccountInfo' - UPI: '#/components/schemas/PaymentUpiAccountInfo' - NGN_ACCOUNT: '#/components/schemas/PaymentNgnAccountInfo' - SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo' - LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo' - SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo' - TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo' - POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo' - BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo' - UsAccountInfo: - type: object - required: - - accountNumber - - routingNumber - - accountCategory - - accountType - properties: - accountType: - type: string - enum: - - US_ACCOUNT - accountNumber: - type: string - description: US bank account number - example: '123456789' - routingNumber: - type: string - description: ACH routing number (9 digits) - example: '987654321' - minLength: 9 - maxLength: 9 - pattern: ^[0-9]{9}$ - accountCategory: - type: string - enum: - - CHECKING - - SAVINGS - description: Type of account (checking or savings) - example: CHECKING - bankName: - type: string - description: Name of the bank - example: Chase Bank - PaymentUsAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/UsAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - PixAccountInfo: - type: object - required: - - pixKey - - pixKeyType - - taxId - - accountType - properties: - accountType: - type: string - enum: - - PIX - pixKey: - type: string - description: PIX key for Brazilian instant payments - example: '55119876543210' - pixKeyType: - type: string - enum: - - CPF - - CNPJ - - EMAIL - - PHONE - - RANDOM - description: Type of PIX key being used - example: PHONE - taxId: - type: string - description: Tax ID of the account holder - example: '1234567890' - PaymentPixAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/PixAccountInfo' - IbanAccountInfo: - type: object - required: - - iban - - swiftBic - - accountType - properties: - accountType: - type: string - enum: - - IBAN - iban: - type: string - description: International Bank Account Number - example: DE89370400440532013000 - minLength: 15 - maxLength: 34 - swiftBic: - type: string - description: SWIFT/BIC code (8 or 11 characters) - example: DEUTDEFF - minLength: 8 - maxLength: 11 - pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - PaymentIbanAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/IbanAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - UpiAccountInfo: - type: object - required: - - vpa - - accountType - properties: - accountType: - type: string - enum: - - UPI - vpa: - type: string - description: Virtual Payment Address for UPI payments - example: somecustomers@okbank - PaymentUpiAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/UpiAccountInfo' - NgnAccountInfo: - type: object - required: - - accountNumber - - bankName - - accountType - properties: - accountType: - type: string - enum: - - NGN_ACCOUNT - accountNumber: - type: string - description: Nigerian bank account number - example: '0123456789' - minLength: 10 - maxLength: 10 - pattern: ^[0-9]{10}$ - bankName: - type: string - description: Name of the bank - example: First Bank of Nigeria - PaymentNgnAccountInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' - - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to properly credit it - example: UMA-Q12345-REF - SparkWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - SPARK_WALLET - address: - type: string - description: Spark wallet address - example: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - PaymentSparkWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - - type: object - required: - - assetType - properties: - assetType: - type: string - description: Type of asset - enum: - - BTC - - USDB - invoice: - type: string - description: Invoice for the payment - example: sparkrt1pgss8ter0fhc4c220f3zftmpz49h8wqte8eg3m5zkrraplgc048jucgszg3ssqgjzqqekv73mmh842yj7drsjwh7t7tz5zt8wf5kghm5v4ehggszppjp5s80cg3qjdzc55g2567tn3lj705hdsr577tg8ah795mlnt6807y657qhkmgfkf9w75p4wz3l8vhua85zdn6ryj32zuj0p00pv2l5z4u47mw6h4s - PaymentLightningInvoiceInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - type: object - required: - - invoice - properties: - invoice: - type: string - description: Invoice for the payment - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - SolanaWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - SOLANA_WALLET - address: - type: string - description: Solana wallet address - example: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - PaymentSolanaWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - - USDT - TronWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - TRON_WALLET - address: - type: string - description: Tron wallet address - example: TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL - PaymentTronWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDT - PolygonWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - POLYGON_WALLET - address: - type: string - description: Polygon eth wallet address - example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' - PaymentPolygonWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - BaseWalletInfo: - type: object - required: - - address - - accountType - properties: - accountType: - type: string - enum: - - BASE_WALLET - address: - type: string - description: Base eth wallet address - example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' - PaymentBaseWalletInfo: - allOf: - - $ref: '#/components/schemas/BasePaymentAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - PaymentAccountType: - type: string - enum: - - CLABE - - US_ACCOUNT - - PIX - - IBAN - - UPI - - NGN_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET - description: Type of payment account or wallet - example: US_ACCOUNT - ClabeAccountInfo: - type: object - required: - - clabeNumber - - accountType - properties: - accountType: - type: string - enum: - - CLABE - clabeNumber: - type: string - description: 18-digit CLABE number (Mexican banking standard) - example: '123456789012345678' - minLength: 18 - maxLength: 18 - pattern: ^[0-9]{18}$ - PaymentInstructions: - type: object - required: - - accountOrWalletInfo - properties: - instructionsNotes: - type: string - description: Additional human-readable instructions for making the payment - example: Please ensure the reference code is included in the payment memo/description field - isPlatformAccount: - type: boolean - description: Indicates whether the account is a platform account or a customer account. - example: true - accountOrWalletInfo: - oneOf: - - title: CLABE Account - $ref: '#/components/schemas/PaymentClabeAccountInfo' - - title: US Bank Account - $ref: '#/components/schemas/PaymentUsAccountInfo' - - title: PIX Account - $ref: '#/components/schemas/PaymentPixAccountInfo' - - title: IBAN Account - $ref: '#/components/schemas/PaymentIbanAccountInfo' - - title: UPI Account - $ref: '#/components/schemas/PaymentUpiAccountInfo' - - title: Spark Wallet - $ref: '#/components/schemas/PaymentSparkWalletInfo' - - title: Lightning Invoice - $ref: '#/components/schemas/PaymentLightningInvoiceInfo' - - title: Solana Wallet - $ref: '#/components/schemas/PaymentSolanaWalletInfo' - - title: Tron Wallet - $ref: '#/components/schemas/PaymentTronWalletInfo' - - title: Polygon Wallet - $ref: '#/components/schemas/PaymentPolygonWalletInfo' - - title: Base Wallet - $ref: '#/components/schemas/PaymentBaseWalletInfo' - discriminator: - propertyName: accountType - mapping: - CLABE: '#/components/schemas/PaymentClabeAccountInfo' - US_ACCOUNT: '#/components/schemas/PaymentUsAccountInfo' - PIX: '#/components/schemas/PaymentPixAccountInfo' - IBAN: '#/components/schemas/PaymentIbanAccountInfo' - UPI: '#/components/schemas/PaymentUpiAccountInfo' - SPARK_WALLET: '#/components/schemas/PaymentSparkWalletInfo' - LIGHTNING: '#/components/schemas/PaymentLightningInvoiceInfo' - SOLANA_WALLET: '#/components/schemas/PaymentSolanaWalletInfo' - TRON_WALLET: '#/components/schemas/PaymentTronWalletInfo' - POLYGON_WALLET: '#/components/schemas/PaymentPolygonWalletInfo' - BASE_WALLET: '#/components/schemas/PaymentBaseWalletInfo' - InternalAccount: - type: object - required: - - id - - balance - - fundingPaymentInstructions - - createdAt - - updatedAt - properties: - id: - type: string - description: The ID of the internal account - example: InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - customerId: - type: string - description: The ID of the customer associated with the internal account. If this field is empty, the internal account belongs to the platform. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - balance: - $ref: '#/components/schemas/CurrencyAmount' - fundingPaymentInstructions: - type: array - description: Payment instructions for funding the account - items: - $ref: '#/components/schemas/PaymentInstructions' - createdAt: - type: string - format: date-time - description: Timestamp when the internal account was created - example: '2025-10-03T12:30:00Z' - updatedAt: - type: string - format: date-time - description: Timestamp when the internal account was last updated - example: '2025-10-03T12:30:00Z' - ExternalAccountStatus: - type: string - enum: - - PENDING - - ACTIVE - - UNDER_REVIEW - - INACTIVE - description: Status of an external account - UsAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UsAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - BaseExternalAccountInfo: - type: object - required: - - accountType - properties: - accountType: - $ref: '#/components/schemas/ExternalAccountType' - discriminator: - propertyName: accountType - mapping: - US_ACCOUNT: '#/components/schemas/UsAccountExternalAccountInfo' - CLABE: '#/components/schemas/ClabeAccountExternalAccountInfo' - PIX: '#/components/schemas/PixAccountExternalAccountInfo' - IBAN: '#/components/schemas/IbanAccountExternalAccountInfo' - UPI: '#/components/schemas/UpiAccountExternalAccountInfo' - NGN_ACCOUNT: '#/components/schemas/NgnAccountExternalAccountInfo' - CAD_ACCOUNT: '#/components/schemas/CadAccountExternalAccountInfo' - GBP_ACCOUNT: '#/components/schemas/GbpAccountExternalAccountInfo' - PHP_ACCOUNT: '#/components/schemas/PhpAccountExternalAccountInfo' - SGD_ACCOUNT: '#/components/schemas/SgdAccountExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' - LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' - BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' - IndividualBeneficiary: - allOf: - - $ref: '#/components/schemas/BaseBeneficiary' - - type: object - required: - - fullName - - birthDate - - nationality - - beneficiaryType - properties: - beneficiaryType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: Individual's full name - example: John Michael Doe - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - BaseBeneficiary: - type: object - required: - - beneficiaryType - properties: - beneficiaryType: - $ref: '#/components/schemas/BeneficiaryType' - address: - $ref: '#/components/schemas/Address' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - BusinessBeneficiary: - allOf: - - $ref: '#/components/schemas/BaseBeneficiary' - - type: object - required: - - legalName - - beneficiaryType - properties: - beneficiaryType: - type: string - enum: - - BUSINESS - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 - BeneficiaryType: - type: string - enum: - - INDIVIDUAL - - BUSINESS - description: Whether the beneficiary is an individual or a business entity - example: INDIVIDUAL - BeneficiaryOneOf: - oneOf: - - title: Individual Beneficiary - $ref: '#/components/schemas/IndividualBeneficiary' - - title: Business Beneficiary - $ref: '#/components/schemas/BusinessBeneficiary' - discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualBeneficiary' - BUSINESS: '#/components/schemas/BusinessBeneficiary' - ClabeAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/ClabeAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - PixAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PixAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - IbanAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/IbanAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - UpiAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/UpiAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - NgnAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/NgnAccountInfo' - - type: object - required: - - purposeOfPayment - - beneficiary - properties: - purposeOfPayment: - type: string - enum: - - GIFT - - SELF - - GOODS_OR_SERVICES - - EDUCATION - - HEALTH_OR_MEDICAL - - REAL_ESTATE_PURCHASE - - LOAN_PAYMENT - - TAX_PAYMENT - - UTILITY_BILL - - DONATION - - TRAVEL - - OTHER - description: Purpose of payment - example: GOODS_OR_SERVICES - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - CadAccountInfo: - type: object - required: - - bankCode - - branchCode - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - CAD_ACCOUNT - bankCode: - type: string - description: Canadian financial institution number (3 digits) - example: '001' - minLength: 3 - maxLength: 3 - pattern: ^[0-9]{3}$ - branchCode: - type: string - description: Transit number identifying the branch (5 digits) - example: '00012' - minLength: 5 - maxLength: 5 - pattern: ^[0-9]{5}$ - accountNumber: - type: string - description: Bank account number (7-12 digits) - example: '1234567' - minLength: 7 - maxLength: 12 - pattern: ^[0-9]{7,12}$ - CadAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/CadAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - GbpAccountInfo: - type: object - required: - - sortCode - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - GBP_ACCOUNT - sortCode: - type: string - description: UK bank sort code (6 digits, may include hyphens) - example: 20-00-00 - pattern: ^[0-9]{2}-?[0-9]{2}-?[0-9]{2}$ - accountNumber: - type: string - description: UK bank account number (8 digits) - example: '12345678' - minLength: 8 - maxLength: 8 - pattern: ^[0-9]{8}$ - GbpAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/GbpAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - PhpAccountInfo: - type: object - required: - - bankName - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - PHP_ACCOUNT - bankName: - type: string - description: Name of the beneficiary's bank - example: BDO Unibank - accountNumber: - type: string - description: Bank account number - example: '001234567890' - PhpAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PhpAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - SgdAccountInfo: - type: object - required: - - bankName - - swiftCode - - accountNumber - - accountType - properties: - accountType: - type: string - enum: - - SGD_ACCOUNT - bankName: - type: string - description: Name of the beneficiary's bank - example: DBS Bank Ltd - swiftCode: - type: string - description: SWIFT/BIC code (8 or 11 characters) - example: DBSSSGSG - minLength: 8 - maxLength: 11 - pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - accountNumber: - type: string - description: Bank account number - example: '0123456789' - SgdAccountExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SgdAccountInfo' - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: '#/components/schemas/BeneficiaryOneOf' - SparkWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SparkWalletInfo' - LightningExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - type: object - description: | - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. - required: - - accountType - properties: - accountType: - type: string - enum: - - LIGHTNING - invoice: - type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: - type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: - type: string - description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. - example: john.doe@lightningwallet.com - SolanaWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/SolanaWalletInfo' - TronWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/TronWalletInfo' - PolygonWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/PolygonWalletInfo' - BaseWalletExternalAccountInfo: - allOf: - - $ref: '#/components/schemas/BaseExternalAccountInfo' - - $ref: '#/components/schemas/BaseWalletInfo' - ExternalAccountType: - type: string - enum: - - US_ACCOUNT - - CLABE - - PIX - - IBAN - - UPI - - NGN_ACCOUNT - - CAD_ACCOUNT - - GBP_ACCOUNT - - PHP_ACCOUNT - - SGD_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET - description: Type of external account or wallet - example: US_ACCOUNT - ExternalAccountInfoOneOf: - oneOf: - - title: US Account - $ref: '#/components/schemas/UsAccountExternalAccountInfo' - - title: CLABE Account - $ref: '#/components/schemas/ClabeAccountExternalAccountInfo' - - title: PIX Account - $ref: '#/components/schemas/PixAccountExternalAccountInfo' - - title: IBAN Account - $ref: '#/components/schemas/IbanAccountExternalAccountInfo' - - title: UPI Account - $ref: '#/components/schemas/UpiAccountExternalAccountInfo' - - title: NGN Account - $ref: '#/components/schemas/NgnAccountExternalAccountInfo' - - title: CAD Account - $ref: '#/components/schemas/CadAccountExternalAccountInfo' - - title: GBP Account - $ref: '#/components/schemas/GbpAccountExternalAccountInfo' - - title: PHP Account - $ref: '#/components/schemas/PhpAccountExternalAccountInfo' - - title: SGD Account - $ref: '#/components/schemas/SgdAccountExternalAccountInfo' - - title: Spark Wallet - $ref: '#/components/schemas/SparkWalletExternalAccountInfo' - - title: Lightning - $ref: '#/components/schemas/LightningExternalAccountInfo' - - title: Solana Wallet - $ref: '#/components/schemas/SolanaWalletExternalAccountInfo' - - title: Tron Wallet - $ref: '#/components/schemas/TronWalletExternalAccountInfo' - - title: Polygon Wallet - $ref: '#/components/schemas/PolygonWalletExternalAccountInfo' - - title: Base Wallet - $ref: '#/components/schemas/BaseWalletExternalAccountInfo' - discriminator: - propertyName: accountType - mapping: - US_ACCOUNT: '#/components/schemas/UsAccountExternalAccountInfo' - CLABE: '#/components/schemas/ClabeAccountExternalAccountInfo' - PIX: '#/components/schemas/PixAccountExternalAccountInfo' - IBAN: '#/components/schemas/IbanAccountExternalAccountInfo' - UPI: '#/components/schemas/UpiAccountExternalAccountInfo' - NGN_ACCOUNT: '#/components/schemas/NgnAccountExternalAccountInfo' - CAD_ACCOUNT: '#/components/schemas/CadAccountExternalAccountInfo' - GBP_ACCOUNT: '#/components/schemas/GbpAccountExternalAccountInfo' - PHP_ACCOUNT: '#/components/schemas/PhpAccountExternalAccountInfo' - SGD_ACCOUNT: '#/components/schemas/SgdAccountExternalAccountInfo' - SPARK_WALLET: '#/components/schemas/SparkWalletExternalAccountInfo' - LIGHTNING: '#/components/schemas/LightningExternalAccountInfo' - SOLANA_WALLET: '#/components/schemas/SolanaWalletExternalAccountInfo' - TRON_WALLET: '#/components/schemas/TronWalletExternalAccountInfo' - POLYGON_WALLET: '#/components/schemas/PolygonWalletExternalAccountInfo' - BASE_WALLET: '#/components/schemas/BaseWalletExternalAccountInfo' - ExternalAccount: - allOf: - - type: object - required: - - id - - status - - currency - - accountInfo - properties: - id: - type: string - description: The system generated identifier of this account - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - customerId: - type: string - description: The customer this account is tied to, or null if the account is on behalf of the platform. - example: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7 - status: - $ref: '#/components/schemas/ExternalAccountStatus' - description: Status of the external account - example: ACTIVE - platformAccountId: - type: string - description: Optional platform-specific identifier for this account - example: acc_123456789 - currency: - type: string - description: The ISO 4217 currency code - example: USD - defaultUmaDepositAccount: - type: boolean - description: Whether this account is the default UMA deposit account for the customer. If true, incoming UMA payments to this customer's UMA address will be automatically deposited into this account instead of the primary internal account. False if not provided. Note that at most, one external account can be set as the default UMA deposit account for a customer. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. - example: false - accountInfo: - $ref: '#/components/schemas/ExternalAccountInfoOneOf' - ExternalAccountCreateRequest: - allOf: - - type: object - required: - - currency - - accountInfo - properties: - customerId: - type: string - description: The ID of the customer for whom to create the external account. If not provided, the external account will be created on behalf of the platform. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: - type: string - description: The ISO 4217 currency code - example: USD - platformAccountId: - type: string - description: Your platform's identifier for the account in your system. This can be used to reference the account by your own identifier. - example: ext_acc_123456 - defaultUmaDepositAccount: - type: boolean - description: Whether to set the external account as the default UMA deposit account. When set to true, incoming payments to this customer's UMA address will be automatically deposited into this external account. False if not provided. Note that only one external account can be set as the default UMA deposit account for a customer, so if there is already a default UMA deposit account, this will override the existing default UMA deposit account. If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. - default: false - accountInfo: - $ref: '#/components/schemas/ExternalAccountInfoOneOf' - PlaidLinkTokenRequest: - type: object - required: - - customerId - properties: - customerId: - type: string - description: The ID of the customer for whom to create the Plaid Link token and external account - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - PlaidLinkTokenResponse: - type: object - required: - - linkToken - - expiration - - callbackUrl - properties: - linkToken: - type: string - description: | - The Plaid Link token to be used to initialize Plaid Link in your application. This token is single-use and expires after the specified expiration time. - example: link-sandbox-af1a0311-da53-4636-b754-dd15cc058176 - expiration: - type: string - format: date-time - description: | - The ISO 8601 timestamp when this link token expires. Link tokens typically expire after 4 hours. - example: '2025-10-05T18:30:00Z' - callbackUrl: - type: string - description: | - The URL where the platform should POST the public_token after the customer completes Plaid Link authentication. This will trigger asynchronous external account creation. The URL includes the linkToken as the path parameter. - example: https://api.lightspark.com/grid/2025-10-13/plaid/callback/{plaid_link_token} - requestId: - type: string - description: A unique identifier for this request, useful for debugging - example: req_abc123def456 - PlaidCallbackRequest: - type: object - required: - - publicToken - properties: - publicToken: - type: string - description: | - The public token returned by Plaid Link after the customer successfully - authenticates and selects an account. - example: public-sandbox-12345678-1234-1234-1234-123456789012 - accountId: - type: string - description: | - Optional Plaid account ID if the customer selected a specific account. - If not provided, the default account will be used. - example: plaid_account_id_123 - Transaction: - type: object - required: - - id - - status - - type - - destination - - customerId - - platformCustomerId - properties: - id: - type: string - description: Unique identifier for the transaction - example: Transaction:019542f5-b3e7-1d02-0000-000000000004 - status: - $ref: '#/components/schemas/TransactionStatus' - type: - $ref: '#/components/schemas/TransactionType' - destination: - $ref: '#/components/schemas/TransactionDestinationOneOf' - customerId: - type: string - description: System ID of the customer (sender for outgoing, recipient for incoming) - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: Platform-specific ID of the customer (sender for outgoing, recipient for incoming) - example: 18d3e5f7b4a9c2 - settledAt: - type: string - format: date-time - description: When the payment was or will be settled - example: '2025-08-15T14:30:00Z' - createdAt: - type: string - format: date-time - description: When the transaction was created - example: '2025-08-15T14:25:18Z' - updatedAt: - type: string - format: date-time - description: When the transaction was last updated - example: '2025-08-15T14:30:00Z' - description: - type: string - description: Optional memo or description for the payment - example: 'Payment for invoice #1234' - counterpartyInformation: - type: object - description: Additional information about the counterparty, if available and relevant to the transaction and platform. Only applicable for transactions to/from UMA addresses. - additionalProperties: true - example: - FULL_NAME: John Sender - BIRTH_DATE: '1985-06-15' - NATIONALITY: DE - discriminator: - propertyName: type - mapping: - INCOMING: '#/components/schemas/IncomingTransaction' - OUTGOING: '#/components/schemas/OutgoingTransaction' - AccountTransactionSource: - allOf: - - $ref: '#/components/schemas/BaseTransactionSource' - - type: object - required: - - accountId - - sourceType - properties: - sourceType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Source account identifier - example: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Source account details - BaseTransactionSource: - type: object - required: - - sourceType - properties: - sourceType: - $ref: '#/components/schemas/TransactionSourceType' - currency: - type: string - description: Currency code for the source - example: USD - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionSource' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionSource' - UmaAddressTransactionSource: - allOf: - - $ref: '#/components/schemas/BaseTransactionSource' - - type: object - required: - - umaAddress - - sourceType - properties: - sourceType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the sender - example: $sender@uma.domain.com - description: UMA address source details - TransactionSourceType: - type: string - enum: - - ACCOUNT - - UMA_ADDRESS - - REALTIME_FUNDING - description: Type of transaction source - example: ACCOUNT - TransactionSourceOneOf: - oneOf: - - title: Account Source - $ref: '#/components/schemas/AccountTransactionSource' - - title: UMA Address Source - $ref: '#/components/schemas/UmaAddressTransactionSource' - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionSource' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionSource' - ReconciliationInstructions: - type: object - required: - - reference - properties: - reference: - type: string - description: Unique reference code that must be included with the payment to match it with the correct incoming transaction - example: UMA-Q12345-REF - IncomingRateDetails: - description: Details about the rate and fees for an incoming transaction. - type: object - required: - - gridApiMultiplier - - gridApiFixedFee - - gridApiVariableFeeRate - - gridApiVariableFeeAmount - properties: - gridApiMultiplier: - type: number - format: double - description: The underlying multiplier from the mSATS to the receiving currency, including variable fees. - exclusiveMinimum: 0 - example: 0.925 - gridApiFixedFee: - type: integer - format: int64 - description: The fixed fee charged by the Grid product to execute the quote in the smallest unit of the receiving currency (eg. cents). - minimum: 0 - example: 10 - gridApiVariableFeeRate: - type: number - format: double - description: The variable fee rate charged by the Grid product to execute the quote as a percentage of the receiving currency amount. - exclusiveMinimum: 0 - example: 0.003 - gridApiVariableFeeAmount: - type: number - format: int64 - description: The variable fee amount charged by the Grid product to execute the quote in the smallest unit of the receiving currency (eg. cents). This is the receiving amount times gridApiVariableFeeRate. - minimum: 0 - example: 30 - IncomingTransactionFailureReason: - type: string - enum: - - LNURLP_FAILED - - PAY_REQUEST_FAILED - - PAYMENT_APPROVAL_WEBHOOK_ERROR - - PAYMENT_APPROVAL_TIMED_OUT - - OFFRAMP_FAILED - - MISSING_MANDATORY_PAYEE_DATA - - QUOTE_EXPIRED - - QUOTE_EXECUTION_FAILED - description: Reason for failure of an incoming transaction. This is used to provide more context on why a transaction failed. If the transaction is not in a failed state, this field is omitted. - IncomingTransaction: - allOf: - - $ref: '#/components/schemas/Transaction' - - type: object - required: - - receivedAmount - properties: - source: - $ref: '#/components/schemas/TransactionSourceOneOf' - receivedAmount: - $ref: '#/components/schemas/CurrencyAmount' - description: Amount received in the recipient's currency - reconciliationInstructions: - $ref: '#/components/schemas/ReconciliationInstructions' - description: Included for all transactions except those with "CREATED" status - rateDetails: - $ref: '#/components/schemas/IncomingRateDetails' - description: Details about the rate and fees for the transaction. - failureReason: - $ref: '#/components/schemas/IncomingTransactionFailureReason' - description: If the transaction failed, this field provides the reason for failure. - Refund: - type: object - required: - - reference - - initiatedAt - properties: - reference: - type: string - description: The unique reference code of the refund - example: UMA-Q12345-REFUND - initiatedAt: - type: string - format: date-time - description: When the refund was initiated - example: '2025-08-15T14:30:00Z' - settledAt: - type: string - format: date-time - description: When the refund was or will be settled - example: '2025-08-15T14:30:00Z' - OutgoingRateDetails: - description: Details about the rate and fees for an outgoing transaction or quote. - type: object - required: - - counterpartyMultiplier - - counterpartyFixedFee - - gridApiMultiplier - - gridApiFixedFee - - gridApiVariableFeeRate - - gridApiVariableFeeAmount - properties: - counterpartyMultiplier: - type: number - format: double - description: The underlying multiplier from mSATs to the receiving currency as returned by the counterparty institution. - exclusiveMinimum: 0 - example: 1.08 - counterpartyFixedFee: - type: integer - format: int64 - description: The fixed fee charged by the counterparty institution to execute the quote in the smallest unit of the receiving currency (eg. cents). - minimum: 0 - example: 10 - gridApiMultiplier: - type: number - format: double - description: The underlying multiplier from the sending currency to mSATS, including variable fees. - exclusiveMinimum: 0 - example: 0.925 - gridApiFixedFee: - type: integer - format: int64 - description: The fixed fee charged by the Grid product to execute the quote in the smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - gridApiVariableFeeRate: - type: number - format: double - description: The variable fee rate charged by the Grid product to execute the quote as a percentage of the sending currency amount. - exclusiveMinimum: 0 - example: 0.003 - gridApiVariableFeeAmount: - type: number - format: int64 - description: The variable fee amount charged by the Grid product to execute the quote in the smallest unit of the sending currency (eg. cents). This is the sending amount times gridApiVariableFeeRate. - minimum: 0 - example: 30 - OutgoingTransactionFailureReason: - type: string - enum: - - QUOTE_EXPIRED - - QUOTE_EXECUTION_FAILED - - LIGHTNING_PAYMENT_FAILED - - FUNDING_AMOUNT_MISMATCH - - COUNTERPARTY_POST_TX_FAILED - - TIMEOUT - description: Reason for failure of an outgoing transaction. This is used to provide more context on why a transaction failed. If the transaction is not in a failed state, this field is omitted. - OutgoingTransaction: - allOf: - - $ref: '#/components/schemas/Transaction' - - type: object - required: - - sentAmount - - paymentInstructions - - source - properties: - source: - $ref: '#/components/schemas/TransactionSourceOneOf' - sentAmount: - $ref: '#/components/schemas/CurrencyAmount' - description: Amount sent in the sender's currency - receivedAmount: - $ref: '#/components/schemas/CurrencyAmount' - description: Amount to be received by recipient in the recipient's currency - exchangeRate: - type: number - description: Number of sending currency units per receiving currency unit. - exclusiveMinimum: 0 - example: 1.08 - fees: - type: integer - format: int64 - description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - quoteId: - type: string - description: The ID of the quote that was used to trigger this payment - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - originalTransactionId: - type: string - description: ID of the original transaction that this transaction is retrying, if applicable - example: Transaction:019542f5-b3e7-1d02-0000-000000000003 - paymentInstructions: - type: array - description: Payment instructions for executing the payment. - items: - $ref: '#/components/schemas/PaymentInstructions' - example: - - accountType: US_ACCOUNT - accountNumber: '1234567890' - routingNumber: '021000021' - bankName: Chase Bank - referenceCode: REF123456 - - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - invoice: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - refund: - $ref: '#/components/schemas/Refund' - description: The refund if transaction was refunded. - rateDetails: - $ref: '#/components/schemas/OutgoingRateDetails' - description: Details about the rate and fees for the transaction. - failureReason: - $ref: '#/components/schemas/OutgoingTransactionFailureReason' - description: If the transaction failed, this field provides the reason for failure. - TransactionStatus: - type: string - enum: - - CREATED - - PENDING - - PROCESSING - - COMPLETED - - REJECTED - - FAILED - - REFUNDED - - EXPIRED - description: Status of a payment transaction - AccountTransactionDestination: - allOf: - - $ref: '#/components/schemas/BaseTransactionDestination' - - type: object - required: - - accountId - - destinationType - properties: - destinationType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Destination account identifier - example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination account details - BaseTransactionDestination: - type: object - required: - - destinationType - properties: - destinationType: - $ref: '#/components/schemas/TransactionDestinationType' - currency: - type: string - description: Currency code for the destination - example: EUR - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionDestination' - UmaAddressTransactionDestination: - allOf: - - $ref: '#/components/schemas/BaseTransactionDestination' - - type: object - required: - - umaAddress - - destinationType - properties: - destinationType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the recipient - example: $receiver@uma.domain.com - description: UMA address destination details - TransactionDestinationType: - type: string - enum: - - ACCOUNT - - UMA_ADDRESS - description: Type of transaction destination - example: ACCOUNT - TransactionDestinationOneOf: - oneOf: - - title: Account Destination - $ref: '#/components/schemas/AccountTransactionDestination' - - title: UMA Address Destination - $ref: '#/components/schemas/UmaAddressTransactionDestination' - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountTransactionDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressTransactionDestination' - CurrencyPreference: - type: object - required: - - currency - - estimatedExchangeRate - - min - - max - properties: - currency: - $ref: '#/components/schemas/Currency' - estimatedExchangeRate: - type: number - description: An estimated exchange rate from the sender's currency to this currency. This is not a locked rate and is subject to change when calling the quotes endpoint. - exclusiveMinimum: 0 - example: 1.08 - min: - type: integer - format: int64 - description: The minimum amount that can be received in this currency. - exclusiveMinimum: 0 - example: 1 - max: - type: integer - format: int64 - description: The maximum amount that can be received in this currency. - exclusiveMinimum: 0 - example: 1000000 - ReceiverLookupResponse: - type: object - required: - - supportedCurrencies - - lookupId - properties: - supportedCurrencies: - type: array - description: List of currencies supported by the receiving account - items: - $ref: '#/components/schemas/CurrencyPreference' - requiredPayerDataFields: - type: array - description: Fields required by the receiving institution about the payer before payment can be completed - items: - $ref: '#/components/schemas/CounterpartyFieldDefinition' - lookupId: - type: string - description: Unique identifier for the lookup. Needed in the subsequent create quote request. - example: Lookup:019542f5-b3e7-1d02-0000-000000000009 - Error412: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 412 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNSUPPORTED_UMA_VERSION | Counterparty doesn't support the Grid UMA version | - enum: - - UNSUPPORTED_UMA_VERSION - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - Error424: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - AccountQuoteSource: - allOf: - - $ref: '#/components/schemas/BaseQuoteSource' - - type: object - required: - - accountId - - sourceType - properties: - sourceType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Source account identifier - example: InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - customerId: - type: string - description: Required when funding from an FBO account to identify the customer on whose behalf the transaction is being initiated. Otherwise, will default to the customerId of the account owner. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - description: Source account details - BaseQuoteSource: - type: object - required: - - sourceType - properties: - sourceType: - $ref: '#/components/schemas/QuoteSourceType' - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountQuoteSource' - REALTIME_FUNDING: '#/components/schemas/RealtimeFundingQuoteSource' - RealtimeFundingQuoteSource: - allOf: - - $ref: '#/components/schemas/BaseQuoteSource' - - type: object - required: - - currency - - sourceType - properties: - sourceType: - type: string - enum: - - REALTIME_FUNDING - customerId: - type: string - description: Source customer ID. If this transaction is being initiated on behalf of a customer, this is required. If customerId is not provided, the quote will be created on behalf of the platform itself. - example: Customer:019542f5-b3e7-1d02-0000-000000000009 - currency: - type: string - description: Currency code for the funding source. See [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) for the full list of supported fiat and crypto currencies. - example: USD - description: Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). This will require manual just-in-time funding using `paymentInstructions` in the response. Because quotes expire quickly, this option is only valid for instant payment methods. Do not try to fund a quote with a non-instant payment method (ACH, etc.). - QuoteSourceType: - type: string - enum: - - ACCOUNT - - REALTIME_FUNDING - description: Type of quote funding source - example: ACCOUNT - QuoteSourceOneOf: - oneOf: - - title: Account - $ref: '#/components/schemas/AccountQuoteSource' - - title: Real-time Funding - $ref: '#/components/schemas/RealtimeFundingQuoteSource' - discriminator: - propertyName: sourceType - mapping: - ACCOUNT: '#/components/schemas/AccountQuoteSource' - REALTIME_FUNDING: '#/components/schemas/RealtimeFundingQuoteSource' - AccountDestination: - allOf: - - $ref: '#/components/schemas/BaseDestination' - - type: object - required: - - accountId - - destinationType - properties: - destinationType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Destination account identifier - example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination account details - BaseDestination: - type: object - required: - - destinationType - properties: - destinationType: - $ref: '#/components/schemas/DestinationType' - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressDestination' - EXTERNAL_ACCOUNT_DETAILS: '#/components/schemas/ExternalAccountDetailsDestination' - UmaAddressDestination: - allOf: - - $ref: '#/components/schemas/BaseDestination' - - type: object - required: - - umaAddress - - destinationType - properties: - destinationType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the recipient - example: $receiver@uma.domain.com - counterpartyInformation: - type: object - description: Information about the recipient, as required by the platform in their configuration. - additionalProperties: true - example: - FULL_NAME: Jane Receiver - BIRTH_DATE: '1990-01-01' - NATIONALITY: FR - currency: - type: string - description: Currency code for the destination. See [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) for the full list of supported fiat and crypto currencies. - example: EUR - description: UMA address destination details - ExternalAccountDetailsDestination: - allOf: - - $ref: '#/components/schemas/BaseDestination' - - type: object - required: - - externalAccountDetails - - destinationType - properties: - destinationType: - type: string - enum: - - EXTERNAL_ACCOUNT_DETAILS - externalAccountDetails: - $ref: '#/components/schemas/ExternalAccountCreateRequest' - description: A convenient destination option which adds the external account and creates the quote in one step rather than first needing to call /external-accounts to add the account. Useful for one-off payments to some destination. See the external accounts endpoints for test values in sandbox mode. - DestinationType: - type: string - enum: - - ACCOUNT - - UMA_ADDRESS - - EXTERNAL_ACCOUNT_DETAILS - description: Type of payment destination - example: ACCOUNT - QuoteDestinationOneOf: - oneOf: - - title: Account - $ref: '#/components/schemas/AccountDestination' - - title: UMA Address - $ref: '#/components/schemas/UmaAddressDestination' - - title: External Account Details - $ref: '#/components/schemas/ExternalAccountDetailsDestination' - discriminator: - propertyName: destinationType - mapping: - ACCOUNT: '#/components/schemas/AccountDestination' - UMA_ADDRESS: '#/components/schemas/UmaAddressDestination' - EXTERNAL_ACCOUNT_DETAILS: '#/components/schemas/ExternalAccountDetailsDestination' - Quote: - type: object - required: - - quoteId - - status - - expiresAt - - createdAt - - source - - destination - - sendingCurrency - - receivingCurrency - - totalSendingAmount - - totalReceivingAmount - - exchangeRate - - feesIncluded - - transactionId - properties: - quoteId: - type: string - description: Unique identifier for this quote - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - description: Current status of the quote - example: PENDING - createdAt: - type: string - format: date-time - description: When this quote was created - example: '2025-10-03T12:00:00Z' - expiresAt: - type: string - format: date-time - description: When this quote expires (typically 1-5 minutes after creation) - example: '2025-10-03T12:05:00Z' - source: - $ref: '#/components/schemas/QuoteSourceOneOf' - destination: - $ref: '#/components/schemas/QuoteDestinationOneOf' - sendingCurrency: - $ref: '#/components/schemas/Currency' - description: Currency for the sending amount - receivingCurrency: - $ref: '#/components/schemas/Currency' - description: Currency for the receiving amount - totalSendingAmount: - type: integer - format: int64 - description: The total amount that will be sent in the smallest unit of the sending currency (eg. cents). - exclusiveMinimum: 0 - example: 123010 - totalReceivingAmount: - type: integer - format: int64 - description: The total amount that will be received in the smallest unit of the receiving currency (eg. cents). - exclusiveMinimum: 0 - example: 1000 - exchangeRate: - type: number - description: Number of sending currency units per receiving currency unit. - exclusiveMinimum: 0 - feesIncluded: - type: integer - format: int64 - description: The fees associated with the quote in the smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - paymentInstructions: - type: array - description: Payment instructions for executing the payment. This is not required when using an internal account source. - items: - $ref: '#/components/schemas/PaymentInstructions' - example: - - accountType: US_ACCOUNT - accountNumber: '1234567890' - routingNumber: '021000021' - bankName: Chase Bank - referenceCode: REF123456 - - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - invoice: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - transactionId: - type: string - description: The ID of the transaction created from this quote. - example: Transaction:019542f5-b3e7-1d02-0000-000000000005 - originalQuoteId: - description: ID of the quote that is being retried - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - rateDetails: - $ref: '#/components/schemas/OutgoingRateDetails' - description: Details about the rate and fees for the transaction. - QuoteLockSide: - type: string - enum: - - SENDING - - RECEIVING - description: The side of the quote which should be locked and specified in the `lockedCurrencyAmount`. For example, if I want to send exactly $5 MXN from my wallet, I would set this to "sending", and the `lockedCurrencyAmount` to 500 (in cents). If I want the receiver to receive exactly $10 USD, I would set this to "receiving" and the `lockedCurrencyAmount` to 10000 (in cents). - QuoteRequest: - type: object - required: - - source - - destination - - lockedCurrencySide - - lockedCurrencyAmount - properties: - lookupId: - type: string - description: |- - Lookup ID from a previous receiver lookup request. If provided, this can make the quote creation more efficient by reusing cached lookup data. - NOTE: This is required for UMA destinations due to counterparty institution requirements. See `senderCustomerInfo` for more information. - example: Lookup:019542f5-b3e7-1d02-0000-000000000009 - source: - $ref: '#/components/schemas/QuoteSourceOneOf' - destination: - $ref: '#/components/schemas/QuoteDestinationOneOf' - lockedCurrencySide: - $ref: '#/components/schemas/QuoteLockSide' - lockedCurrencyAmount: - type: integer - format: int64 - description: The amount to send/receive in the smallest unit of the locked currency (eg. cents). See `lockedCurrencySide` for more information. - exclusiveMinimum: 0 - maximum: 9000000000000000 - example: 1000 - immediatelyExecute: - type: boolean - description: Whether to immediately execute the quote after creation. If true, the quote will be executed and the transaction will be created at the current exchange rate. It should only be used if you don't want to lock and view rate details before executing the quote. If you are executing a pre-existing quote, use the `/quotes/{quoteId}/execute` endpoint instead. This is false by default. - example: false - description: - type: string - description: Optional description/memo for the transfer - example: 'Invoice #1234 payment' - senderCustomerInfo: - type: object - additionalProperties: true - description: | - Only relevant for UMA destinations. - Key-value pairs of information about the sender which was requested by the counterparty (recipient) institution. - Any fields specified in `requiredPayerDataFields` from the response of the `/receiver/uma/{receiverUmaAddress}` (lookupUma) endpoint - MUST be provided here if they were requested. If the counterparty (recipient) institution did not request any information, this field can be omitted. - example: - FULL_NAME: Jane Receiver - NATIONALITY: FR - TestWebhookResponse: - type: object - required: - - response_status - properties: - url: - type: string - format: uri - description: URL where the webhook was sent - example: https://api.mycompany.com/webhooks/uma - response_status: - type: integer - description: The HTTP status code returned by the webhook endpoint - example: 200 - response_body: - type: string - description: The raw body content returned by the webhook endpoint in response to the request - GridError: - type: object - title: GridError - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - BulkCustomerImportErrorEntry: - allOf: - - $ref: '#/components/schemas/GridError' - - type: object - description: Error information for a failed bulk import entry - required: - - correlationId - properties: - correlationId: - type: string - description: Platform customer ID or row number for the failed entry - example: biz456 - BulkCustomerImportJob: - type: object - required: - - jobId - - status - - progress - properties: - jobId: - type: string - description: Unique identifier for the bulk import job - example: Job:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - description: Current status of the job - example: PROCESSING - progress: - type: object - required: - - total - - processed - - successful - - failed - properties: - total: - type: integer - description: Total number of customers to process - example: 5000 - processed: - type: integer - description: Number of customers processed so far - example: 2500 - successful: - type: integer - description: Number of customers successfully created - example: 2450 - failed: - type: integer - description: Number of customers that failed to create - example: 50 - errors: - type: array - description: Detailed error information for failed entries - items: - $ref: '#/components/schemas/BulkCustomerImportErrorEntry' - completedAt: - type: string - format: date-time - description: Timestamp when the job completed (only present for COMPLETED or FAILED status) - example: '2025-08-15T14:32:00Z' - UmaInvitation: - type: object - required: - - code - - createdAt - - inviterUma - - status - - url - properties: - code: - type: string - description: The unique code of the invitation - example: 019542f5 - createdAt: - type: string - format: date-time - description: When the invitation was created - example: '2025-09-01T14:30:00Z' - claimedAt: - type: string - format: date-time - description: When the invitation was claimed if it has been claimed - example: '2025-09-01T14:30:00Z' - url: - type: string - description: The URL where this invitation can be claimed. - example: https://uma.me/i/019542f5 - expiresAt: - type: string - format: date-time - description: When the invitation expires (if at all) - example: '2025-09-01T14:30:00Z' - inviterUma: - type: string - description: The UMA address of the inviter - example: $inviter@uma.domain - inviteeUma: - type: string - description: The UMA address of the invitee - example: $invitee@uma.domain - status: - type: string - enum: - - PENDING - - CLAIMED - - EXPIRED - - CANCELLED - description: The status of the invitation - example: PENDING - firstName: - type: string - description: The inviter's first name. Will be displayed when the recipient clicks the invite link - example: Jane - amountToSend: - $ref: '#/components/schemas/CurrencyAmount' - description: |- - The amount to send to the invitee when the invitation is claimed. This is optional and if not provided, the invitee will not receive any amount. Note that the actual sending of the amount must be done by the inviter platform once the INVITATION_CLAIMED webhook is received. If the inviter platform either does not send the payment or the payment fails, the invitee will not receive this amount. This field is primarily used for display purposes on the claiming side of the invitation. - This field is useful for "send-by-link" style customer flows where an inviter can send a payment simply by sharing a link without knowing the receiver's UMA address. Note that these sends can only be sender-locked, meaning that the sender will not know ahead of time how much the receiver will receive in the receiving currency. - Error403: - type: object - required: - - message - - status - - code - properties: - status: - type: integer - enum: - - 403 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | FORBIDDEN | Insufficient permissions | - | USER_NOT_READY | Customer exists but is not ready for operation | - | COUNTERPARTY_NOT_ALLOWED | Counterparty has not been enabled for your account | - | VELOCITY_LIMIT_EXCEEDED | Counterparty has exceeded velocity limits | - enum: - - FORBIDDEN - - USER_NOT_READY - - COUNTERPARTY_NOT_ALLOWED - - VELOCITY_LIMIT_EXCEEDED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true - UmaProvider: - type: object - properties: - name: - type: string - description: Name of the UMA Provider - example: Lightspark Group - supportedRegions: - type: array - items: - type: string - description: Region(s) this UMA Provider operates in - example: - - US - domain: - type: string - description: Domain this VASP uses for UMA addresses - example: uma.me - logoUrl: - type: string - description: Logo URL for the VASP - format: uri - example: https://uma.me/logo.png - supportedCurrencies: - type: array - items: - $ref: '#/components/schemas/Currency' - description: List of currencies supported by this UMA Provider - example: - - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - lei: - type: string - description: Legal Entity Identifier for the UMA Provider - example: 5493001KJTIIGC8Y1R12 - allowListStatus: - type: boolean - description: Whether this UMA Provider is on your allow list - example: true - Permission: - type: string - enum: - - VIEW - - TRANSACT - - MANAGE - description: 'Permission of an API token that determines what actions the token can perform: VIEW: Can view all data, including platform config, customers and transactions TRANSACT: Can send payments MANAGE: Can manage platform config, api tokens and customers' - ApiToken: - type: object - required: - - id - - name - - permissions - - clientId - - createdAt - - updatedAt - properties: - id: - type: string - description: System-generated unique identifier - example: Token:019542f5-b3e7-1d02-0000-000000000001 - name: - type: string - description: Name of the token - example: Sandbox read-only token - permissions: - type: array - description: A list of permissions granted to the token - items: - $ref: '#/components/schemas/Permission' - clientId: - type: string - description: An opaque identifier that should be used as a client_id (or username) in the HTTP Basic Authentication scheme when issuing http requests to Grid. - example: 01947d2284054f890000e63bca4810df - clientSecret: - type: string - description: The secret that should be used to authenticate against Grid API. This secret is not stored and will never be available again after creation. Platform must keep this secret secure as it grants access to the account. - example: ed0ad25881e234cc28fb2dec0a4fe64e4172 - createdAt: - type: string - format: date-time - description: Creation timestamp - example: '2025-07-21T17:32:28Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - example: '2025-07-21T17:32:28Z' - IncomingPaymentWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - transaction - properties: - transaction: - $ref: '#/components/schemas/IncomingTransaction' - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: INCOMING_PAYMENT - requestedReceiverCustomerInfoFields: - type: array - items: - $ref: '#/components/schemas/CounterpartyFieldDefinition' - description: Information required by the sender's VASP about the recipient. Platform must provide these in the 200 OK response if approving. Note that this only includes fields which Grid does not already have from initial customer registration. - BaseWebhook: - type: object - required: - - timestamp - - webhookId - - type - properties: - timestamp: - type: string - format: date-time - description: ISO8601 timestamp when the webhook was sent (can be used to prevent replay attacks) - example: '2025-08-15T14:32:00Z' - webhookId: - type: string - description: Unique identifier for this webhook delivery (can be used for idempotency) - example: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - discriminator: - propertyName: type - mapping: - INCOMING_PAYMENT: '#/components/schemas/IncomingPaymentWebhook' - OUTGOING_PAYMENT: '#/components/schemas/OutgoingPaymentWebhook' - TEST: '#/components/schemas/TestWebhookRequest' - BULK_UPLOAD: '#/components/schemas/BulkUploadWebhookRequest' - INVITATION_CLAIMED: '#/components/schemas/InvitationClaimedWebhook' - KYC_STATUS: '#/components/schemas/KycStatusWebhook' - WebhookType: - type: string - enum: - - INCOMING_PAYMENT - - OUTGOING_PAYMENT - - TEST - - BULK_UPLOAD - - INVITATION_CLAIMED - - KYC_STATUS - - ACCOUNT_STATUS - description: Type of webhook event, used by the receiver to identify which webhook is being received - OutgoingPaymentWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - transaction - properties: - transaction: - $ref: '#/components/schemas/OutgoingTransaction' - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: OUTGOING_PAYMENT - TestWebhookRequest: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - properties: - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: TEST - BulkUploadWebhookRequest: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - bulkCustomerImportJob - properties: - bulkCustomerImportJob: - $ref: '#/components/schemas/BulkCustomerImportJob' - type: - $ref: '#/components/schemas/WebhookType' - description: Type of webhook event - example: BULK_UPLOAD - InvitationClaimedWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - invitation - properties: - invitation: - $ref: '#/components/schemas/UmaInvitation' - type: - type: string - enum: - - INVITATION_CLAIMED - description: Type of webhook event - example: INVITATION_CLAIMED - KycStatusWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - customerId - - kycStatus - properties: - customerId: - type: string - description: System generated id of the customer - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - kycStatus: - $ref: '#/components/schemas/KycStatus' - IncomingPaymentWebhookResponse: - type: object - properties: - receiverCustomerInfo: - type: object - additionalProperties: true - description: Information about the recipient, provided by the platform if requested in the webhook via `requestedReceiverCustomerInfoFields` and the payment is approved. - IncomingPaymentWebhookForbiddenResponse: - allOf: - - $ref: '#/components/schemas/GridError' - - type: object - properties: - reason: - type: string - description: Optional reason for rejecting the payment. This is just for debugging purposes or can be used for a platform's own purposes. - example: RESTRICTED_JURISDICTION - IncomingPaymentWebhookUnprocessableResponse: - allOf: - - $ref: '#/components/schemas/GridError' - - type: object - properties: - requiredFields: - type: array - items: - type: string - description: List of fields that are required by the platform, but are not present in the counterparty information. - example: - - TAX_ID - - REGISTRATION_NUMBER - AccountStatusWebhook: - allOf: - - $ref: '#/components/schemas/BaseWebhook' - - type: object - required: - - accountId - properties: - accountId: - type: string - description: The id of the account whose balance has changed - oldBalance: - $ref: '#/components/schemas/CurrencyAmount' - newBalance: - $ref: '#/components/schemas/CurrencyAmount' - customerId: - type: string - description: The ID of the customer associated with the internal account - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: The ID of the customer as associated in your platform - example: 019542f5-b3e7-1d02-0000-000000000001 diff --git a/openapi/README.md b/openapi/README.md deleted file mode 100644 index 60d201b9..00000000 --- a/openapi/README.md +++ /dev/null @@ -1,467 +0,0 @@ -# API Design Guidelines - -This readme is a continually evolving document meant to provide API design best practices for the following purposes. - -1. API feels consistent -2. Preempt known bad patterns -3. Make new APIs easy to define - ---- -## OpenAPI schema version -This guide uses [OpenAPI schema 3.1](https://spec.openapis.org/oas/v3.1.0.html). -- Imagine you're teaching a customer how to use our API. How might you structure the API to make it easy to explain and understand? -- Since we don't know exactly how customers will use the API, how might we make it flexible? -- Can integrators guess how your API works based on how other features work? -- When something goes wrong, can the integrator figure out why? - -## Directory Structure - -``` -openapi/ -├── openapi.yaml # Main spec file with paths, tags, and security -├── paths/ # Endpoint definitions organized by resource -│ ├── customers/ # Customer-related endpoints -│ ├── quotes/ # Quote-related endpoints -│ ├── transactions/ # Transaction-related endpoints -│ └── {resource}/ # Other resource endpoints -├── components/ -│ └── schemas/ -│ ├── common/ # Shared schemas (Address, Currency, etc.) -│ ├── customers/ # Customer schemas -│ ├── transactions/ # Transaction schemas -│ ├── errors/ # Error schemas (Error400.yaml, Error401.yaml, etc.) -│ └── {resource}/ # Other resource schemas -└── webhooks/ # Webhook event definitions -``` - -### File Naming Conventions - -| Pattern | Usage | Example | -|---------|-------|---------| -| `{resource}.yaml` | Collection endpoints | `customers.yaml` | -| `{resource}_{pathParam}.yaml` | Single resource endpoints | `customers_{customerId}.yaml` | -| `{resource}_{pathParam}_{action}.yaml` | Action endpoints | `quotes_{quoteId}_execute.yaml` | - ---- - -## Versioning - -We version by dates but SDKs still use semver. - -### Version Format -- **API Version**: `YYYY-MM-DD` format (e.g., `2025-10-13`) -- **Server URL**: Version is included in the path: `https://api.lightspark.com/grid/2025-10-13` -- **SDK Version**: Follows semver (`1.0.0`, `1.1.0`, `2.0.0`) - -### What's Considered a Breaking Change -- New required field on request -- Removing a field from response -- Changing a field name -- Changing a resource name or path -- Changing the type of a field -- Removing an enum value - -When you release an SDK, Stainless will flag breaking changes. - -### What's Not a Breaking Change -- Making a required field optional -- Adding a new optional field -- Adding a new enum value -- Adding a new endpoint -- Adding a new response field - -### Deprecation Policy -*TBD! BUT initial thoughts* -1. Mark deprecated endpoints with `deprecated: true` in OpenAPI spec -2. Document deprecation in changelog and SDK release notes -3. Maintain deprecated endpoints for at least X months -4. Communicate migration path in documentation - -### Instead of a breaking change, you can -- Add new optional fields instead of modifying existing ones -- Create a new endpoint if behavior must change significantly - ---- - -## Naming Conventions - -### Resources -- Use **plural nouns** for resource names: `/customers` not `/customer` -- Exception: Use singular when there can only be one (e.g., `/config`) - -### Identifiers -ID values should be prefixed with the resource type to help users identify the resource type in their system. - -| Resource | ID Format | Example | -|----------|-----------|---------| -| Customer | `Customer:{uuid}` | `Customer:019542f5-b3e7-1d02-0000-000000000001` | -| Quote | `Quote:{uuid}` | `Quote:019542f5-b3e7-1d02-0000-000000000001` | -| Transaction | `Transaction:{uuid}` | `Transaction:019542f5-b3e7-1d02-0000-000000000001` | - -### Field Naming - -| Element | Convention | Example | -|---------|------------|---------| -| Fields | camelCase | `platformCustomerId`, `hasMore`, `nextCursor` | -| Enums | UPPER_SNAKE_CASE | `INDIVIDUAL`, `BUSINESS`, `INVALID_INPUT` | -| Query params | camelCase | `startDate`, `sortOrder`, `customerId` | -| Path params | camelCase | `customerId`, `quoteId`, `transactionId` | - -Use a type hint where it makes sense eg startDate, customerId. - -### Common Fields - -| Name | Description | -|------|-------------| -| `id` | Unique identifier for the resource (prefixed format) | -| `createdAt` | ISO 8601 timestamp when the resource was created | -| `updatedAt` | ISO 8601 timestamp when the resource was last updated | - ---- - -## Service Patterns - -### States - -Resources with lifecycle states (e.g., transactions, quotes, invitations) should document: -1. All possible states -2. Valid state transitions -3. What triggers each transition -4. Which states are terminal (no further transitions) - -#### State Naming Conventions - -- Use UPPER_SNAKE_CASE for state values -- Use past tense for terminal states: `COMPLETED`, `FAILED`, `CANCELLED` -- Use present continuous for in-progress states: `PENDING`, `PROCESSING` -- Use `CREATED` for initial states (not `NEW`) - -#### Documenting State Machines - -Include a [Mermaid State Diagram](https://mermaid.js.org/syntax/stateDiagram.html) in the schema description to visualize transitions: - -```yaml -status: - type: string - enum: - - CREATED - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - description: | - Current status of the transaction. - - ```mermaid - stateDiagram-v2 - [*] --> CREATED - CREATED --> PENDING: Quote executed - PENDING --> PROCESSING: Payment initiated - PROCESSING --> COMPLETED: Payment successful - PROCESSING --> FAILED: Payment failed - CREATED --> EXPIRED: Quote expired - PENDING --> EXPIRED: Timeout - ``` - - | Status | Description | Terminal | - |--------|-------------|----------| - | CREATED | Transaction created, awaiting execution | No | - | PENDING | Awaiting payment or confirmation | No | - | PROCESSING | Payment in progress | No | - | COMPLETED | Payment successful | Yes | - | FAILED | Payment failed | Yes | - | EXPIRED | Quote or transaction expired | Yes | -``` - -#### Terminal vs Non-Terminal States - -- **Terminal states**: No further transitions possible. Mark these clearly in documentation. -- **Non-terminal states**: Can transition to other states. Document what triggers each transition. - -#### Example: Transaction Status - -```mermaid -stateDiagram-v2 - [*] --> CREATED - CREATED --> PENDING: execute quote - CREATED --> EXPIRED: quote expires - PENDING --> PROCESSING: payment initiated - PENDING --> EXPIRED: timeout - PROCESSING --> COMPLETED: success - PROCESSING --> FAILED: error - PROCESSING --> REJECTED: compliance rejection - COMPLETED --> REFUNDED: refund issued -``` - -#### Example: Quote Status - -```mermaid -stateDiagram-v2 - [*] --> PENDING - PENDING --> PROCESSING: execute called - PENDING --> EXPIRED: TTL exceeded - PROCESSING --> COMPLETED: transaction created - PROCESSING --> FAILED: execution error -``` - -### Resource Actions - -When enabling an action on a resource, use `POST /{resource}/{id}/{action}`: - -``` -POST /quotes/{quoteId}/execute -POST /invitations/{invitationCode}/claim -POST /invitations/{invitationCode}/cancel -POST /transactions/{transactionId}/approve -POST /transactions/{transactionId}/reject -``` - -### Linked Resources vs Sub-Resources - -Prefer flat, linked resources over deeply nested sub-resources: - -``` -# Preferred: Flat with query parameter -GET /external-accounts?customerId={customerId} - -# Avoid: Sub-resourcing -GET /customers/{customerId}/external-accounts -``` - -If an integrator wanted to get all external accounts, they would need to make one request for each customerId. - -### Filtering - -- Use query parameters for filtering: `?status=PENDING&customerId=Customer:123` -- Support duplicated values for multifilter: `?customerId={id_1}&customerId={id_2}` -- Use ISO 8601 for date filters: `?startDate=2025-01-01T00:00:00Z` - -### Sorting - -- Use `sortOrder` parameter with values `asc` or `desc` -- Default to `desc` (most recent first) for time-based resources - ---- - -## OpenAPI Best Practices - -### Discriminators and Polymorphism - -OneOfs must include a discriminator: - -```yaml -oneOf: - - $ref: '#/components/schemas/IndividualCustomer' - - $ref: '#/components/schemas/BusinessCustomer' -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: '#/components/schemas/IndividualCustomer' - BUSINESS: '#/components/schemas/BusinessCustomer' -``` - -Also: -- Define the discriminator property as a separate schema with an enum so generated client libraries include an enum class (see `openapi/components/schemas/customers/CustomerType.yaml`, used by `customerType` in `Customer` and `CustomerCreateRequest`). -- Each discriminated schema must include the discriminator field with a single-value enum, because the OpenAPI generator does not reliably infer it from the discriminator (see `IndividualCustomerFields` included by `IndividualCustomer.yaml` and `IndividualCustomerCreateRequest.yaml`, where `customerType` is `enum: [INDIVIDUAL]`). - -### Composition - -- Use `allOf` for extending base schemas - -### Documentation in OpenAPI - -- Add `description` to every endpoint, parameter, and schema field -- Include realistic `example` values that can be copied -- Use markdown tables in descriptions to document enum values: - -```yaml -code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNAUTHORIZED | Issue with API credentials | - | INVALID_SIGNATURE | Signature header is invalid | - enum: - - UNAUTHORIZED - - INVALID_SIGNATURE -``` - -### Validation - -- Use `minimum`, `maximum` for numeric bounds -- Use `format` for standard types: `date-time`, `email`, `uri`, `uuid` -- Use `enum` for fixed value sets -- Use `pattern` for regex validation when needed - ---- - -## HTTP Methods and Status Codes - -### HTTP Methods - -| Method | Usage | Success Code | -|--------|-------|--------------| -| GET | Retrieve resource(s) | 200 | -| POST | Create resource or execute action | 201 (create), 200 (action) | -| PATCH | Partial update of a resource | 200 | -| DELETE | Remove a resource | 204 | - -### Success Status Codes - -| Code | Usage | -|------|-------| -| 200 | Successful GET, PATCH, or action POST | -| 201 | Resource created successfully | -| 204 | Resource deleted successfully (no content) | - -### Error Status Codes -Generally 4xx errors indicate an issue with the integrators request and 5xx errors indicate an issue with our services. Our client libraries will automatically retry 5xx responses. - -| Code | Meaning | When to Use | -|------|---------|-------------| -| 400 | Bad Request | Invalid input, validation errors | -| 401 | Unauthorized | Missing or invalid authentication | -| 403 | Forbidden | Valid auth but insufficient permissions | -| 404 | Not Found | Resource doesn't exist | -| 409 | Conflict | Resource already exists, state conflict | -| 412 | Precondition Failed | Conditional request failed | -| 424 | Failed Dependency | Dependent service failure | -| 500 | Internal Server Error | Unexpected server error | -| 501 | Not Implemented | Feature not yet available | - ---- - -## Pagination - -We use cursor-based pagination for all list endpoints. This pattern is compatible with Stainless SDK auto-pagination. - -### Request Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `limit` | integer | 20 | Maximum results to return (max: 100) | -| `cursor` | string | - | Opaque cursor from previous response | - -### Response Structure - -```json -{ - "data": [...], - "hasMore": true, - "nextCursor": "eyJpZCI6IjEyMzQ1In0=", - "totalCount": 150 -} -``` - -| Field | Type | Description | -|-------|------|-------------| -| `data` | array | Array of resources | -| `hasMore` | boolean | Whether more results exist | -| `nextCursor` | string | Cursor for next page (only if `hasMore: true`) | -| `totalCount` | integer | Total matching resources (excluding pagination) | - ---- - -## Error Handling - -### Error Schema Organization - -Error codes across services are aggregated by HTTP status code in `components/schemas/errors/`. As an example both `/customers` and `/transactions` may have unique 400 error codes. All of these would be aggregated in the `Error400.yaml` definition. Stainless then uses these to generate [unique throwable errors](https://github.com/lightsparkdev/umaaas-kotlin-sdk/blob/main/umaaas-kotlin-core/src/main/kotlin/com/lightspark/umaaas/errors/SpecificApiErrors.kt) - -| File | HTTP Status | Example Codes | -|------|-------------|---------------| -| `Error400.yaml` | 400 Bad Request | `INVALID_INPUT`, `INVALID_AMOUNT`, `MISSING_MANDATORY_USER_INFO` | -| `Error401.yaml` | 401 Unauthorized | `UNAUTHORIZED`, `INVALID_SIGNATURE` | -| `Error403.yaml` | 403 Forbidden | `FORBIDDEN`, `INSUFFICIENT_PERMISSIONS` | -| `Error404.yaml` | 404 Not Found | `NOT_FOUND`, `CUSTOMER_NOT_FOUND` | -| `Error409.yaml` | 409 Conflict | `CONFLICT`, `ALREADY_EXISTS` | -| `Error500.yaml` | 500 Internal Error | `INTERNAL_ERROR` | - -### Error Response Structure - -All errors follow a consistent structure: - -```json -{ - "status": 400, - "code": "INVALID_INPUT", - "message": "The provided email address is not valid", - "details": { - "field": "email", - "reason": "Invalid format" - } -} -``` - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `status` | integer | Yes | HTTP status code | -| `code` | string | Yes | Machine-readable error code (SCREAMING_SNAKE_CASE) | -| `message` | string | Yes | Human-readable error message | -| `details` | object | No | Additional context about the error | - -### Error Code Guidelines - -- Use SCREAMING_SNAKE_CASE for error codes -- Make codes specific and actionable (e.g., `INVALID_BANK_ACCOUNT` not just `INVALID`) -- Document all codes with descriptions in the Error schema -- Include markdown tables in the `description` field listing all codes - -### Adding New Error Codes - -1. Identify the appropriate HTTP status code -2. Add the new code to the corresponding `Error{StatusCode}.yaml` enum -3. Add a description in the markdown table within the `code` field description -4. Update SDK error handling if needed - ---- - -## Stainless SDK Patterns - -Our SDKs are generated by [Stainless](https://www.stainless.com/) from the OpenAPI spec. - -### SDK Usage Example - -```typescript -import Grid from 'grid'; - -const client = new Grid({ - username: process.env['GRID_USERNAME'], - password: process.env['GRID_PASSWORD'], -}); - -// Auto-pagination -for await (const customer of client.customers.list()) { - console.log(customer.id); -} - -// Error handling -try { - await client.quotes.execute('Quote:invalid'); -} catch (err) { - if (err instanceof Grid.NotFoundError) { - console.log('Quote not found'); - } -} -``` -Stainless also generates [API reference SDK examples](https://www.stainless.com/docs/guides/integrate-docs#how-stainless-generates-sdk-code-snippets) for our Mintlify documentation - ---- - -## Redocly CLI - -We use Redocly CLI to bundle and lint the OpenAPI schema. - -### Bundling - -```bash -npx @redocly/cli bundle openapi/openapi.yaml -o openapi.yaml -``` - -### Useful Features - -- [Hide APIs for internal use](https://redocly.com/docs/cli/guides/hide-apis) -- Lint rules configured in `.redocly.yaml` -- Ignore specific lint rules in `.redocly.lint-ignore.yaml` diff --git a/openapi/components/schemas/common/Address.yaml b/openapi/components/schemas/common/Address.yaml deleted file mode 100644 index e4787e7e..00000000 --- a/openapi/components/schemas/common/Address.yaml +++ /dev/null @@ -1,30 +0,0 @@ -type: object -required: - - line1 - - postalCode - - country -properties: - line1: - type: string - description: Street address line 1 - example: 123 Main Street - line2: - type: string - description: Street address line 2 - example: Apt 4B - city: - type: string - description: City - example: San Francisco - state: - type: string - description: State/Province/Region - example: CA - postalCode: - type: string - description: Postal/ZIP code - example: '94105' - country: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US diff --git a/openapi/components/schemas/common/BasePaymentAccountInfo.yaml b/openapi/components/schemas/common/BasePaymentAccountInfo.yaml deleted file mode 100644 index 0549480e..00000000 --- a/openapi/components/schemas/common/BasePaymentAccountInfo.yaml +++ /dev/null @@ -1,21 +0,0 @@ -type: object -required: - - accountType -properties: - accountType: - $ref: ./PaymentAccountType.yaml -discriminator: - propertyName: accountType - mapping: - CLABE: ./PaymentClabeAccountInfo.yaml - US_ACCOUNT: ./PaymentUsAccountInfo.yaml - PIX: ./PaymentPixAccountInfo.yaml - IBAN: ./PaymentIbanAccountInfo.yaml - UPI: ./PaymentUpiAccountInfo.yaml - NGN_ACCOUNT: ./PaymentNgnAccountInfo.yaml - SPARK_WALLET: ./PaymentSparkWalletInfo.yaml - LIGHTNING: ./PaymentLightningInvoiceInfo.yaml - SOLANA_WALLET: ./PaymentSolanaWalletInfo.yaml - TRON_WALLET: ./PaymentTronWalletInfo.yaml - POLYGON_WALLET: ./PaymentPolygonWalletInfo.yaml - BASE_WALLET: ./PaymentBaseWalletInfo.yaml diff --git a/openapi/components/schemas/common/BaseWalletInfo.yaml b/openapi/components/schemas/common/BaseWalletInfo.yaml deleted file mode 100644 index 6a3ef9a8..00000000 --- a/openapi/components/schemas/common/BaseWalletInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - address - - accountType -properties: - accountType: - type: string - enum: - - BASE_WALLET - address: - type: string - description: Base eth wallet address - example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' diff --git a/openapi/components/schemas/common/CadAccountInfo.yaml b/openapi/components/schemas/common/CadAccountInfo.yaml deleted file mode 100644 index 5504fe99..00000000 --- a/openapi/components/schemas/common/CadAccountInfo.yaml +++ /dev/null @@ -1,32 +0,0 @@ -type: object -required: - - bankCode - - branchCode - - accountNumber - - accountType -properties: - accountType: - type: string - enum: - - CAD_ACCOUNT - bankCode: - type: string - description: Canadian financial institution number (3 digits) - example: '001' - minLength: 3 - maxLength: 3 - pattern: ^[0-9]{3}$ - branchCode: - type: string - description: Transit number identifying the branch (5 digits) - example: '00012' - minLength: 5 - maxLength: 5 - pattern: ^[0-9]{5}$ - accountNumber: - type: string - description: Bank account number (7-12 digits) - example: '1234567' - minLength: 7 - maxLength: 12 - pattern: ^[0-9]{7,12}$ diff --git a/openapi/components/schemas/common/ClabeAccountInfo.yaml b/openapi/components/schemas/common/ClabeAccountInfo.yaml deleted file mode 100644 index 6f70a2f4..00000000 --- a/openapi/components/schemas/common/ClabeAccountInfo.yaml +++ /dev/null @@ -1,16 +0,0 @@ -type: object -required: - - clabeNumber - - accountType -properties: - accountType: - type: string - enum: - - CLABE - clabeNumber: - type: string - description: 18-digit CLABE number (Mexican banking standard) - example: '123456789012345678' - minLength: 18 - maxLength: 18 - pattern: ^[0-9]{18}$ diff --git a/openapi/components/schemas/common/CounterpartyFieldDefinition.yaml b/openapi/components/schemas/common/CounterpartyFieldDefinition.yaml deleted file mode 100644 index cbe1217f..00000000 --- a/openapi/components/schemas/common/CounterpartyFieldDefinition.yaml +++ /dev/null @@ -1,11 +0,0 @@ -type: object -properties: - name: - $ref: ../customers/CustomerInfoFieldName.yaml - mandatory: - type: boolean - description: Whether the field is mandatory - example: true -required: - - name - - mandatory diff --git a/openapi/components/schemas/common/Currency.yaml b/openapi/components/schemas/common/Currency.yaml deleted file mode 100644 index a2510447..00000000 --- a/openapi/components/schemas/common/Currency.yaml +++ /dev/null @@ -1,22 +0,0 @@ -type: object -properties: - code: - type: string - description: >- - Three-letter currency code (ISO 4217) for fiat currencies. Some - cryptocurrencies may use their own ticker symbols (e.g. "BTC" for - Bitcoin, "USDC" for USDC, etc.) - example: USD - name: - type: string - description: Full name of the currency - example: United States Dollar - symbol: - type: string - description: Symbol of the currency - example: $ - decimals: - type: integer - description: Number of decimal places for the currency - minimum: 0 - example: 2 diff --git a/openapi/components/schemas/common/CurrencyAmount.yaml b/openapi/components/schemas/common/CurrencyAmount.yaml deleted file mode 100644 index 689596bf..00000000 --- a/openapi/components/schemas/common/CurrencyAmount.yaml +++ /dev/null @@ -1,14 +0,0 @@ -type: object -required: - - amount - - currency -properties: - amount: - type: integer - format: int64 - description: >- - Amount in the smallest unit of the currency (e.g., cents for USD/EUR, - satoshis for BTC) - example: 12550 - currency: - $ref: ./Currency.yaml diff --git a/openapi/components/schemas/common/Error.yaml b/openapi/components/schemas/common/Error.yaml deleted file mode 100644 index b0eb270c..00000000 --- a/openapi/components/schemas/common/Error.yaml +++ /dev/null @@ -1,11 +0,0 @@ -type: object -properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - description: Additional error details diff --git a/openapi/components/schemas/common/GbpAccountInfo.yaml b/openapi/components/schemas/common/GbpAccountInfo.yaml deleted file mode 100644 index 68a9a163..00000000 --- a/openapi/components/schemas/common/GbpAccountInfo.yaml +++ /dev/null @@ -1,22 +0,0 @@ -type: object -required: - - sortCode - - accountNumber - - accountType -properties: - accountType: - type: string - enum: - - GBP_ACCOUNT - sortCode: - type: string - description: UK bank sort code (6 digits, may include hyphens) - example: '20-00-00' - pattern: '^[0-9]{2}-?[0-9]{2}-?[0-9]{2}$' - accountNumber: - type: string - description: UK bank account number (8 digits) - example: '12345678' - minLength: 8 - maxLength: 8 - pattern: ^[0-9]{8}$ diff --git a/openapi/components/schemas/common/GridError.yaml b/openapi/components/schemas/common/GridError.yaml deleted file mode 100644 index 4f956b62..00000000 --- a/openapi/components/schemas/common/GridError.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -title: GridError -properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/common/IbanAccountInfo.yaml b/openapi/components/schemas/common/IbanAccountInfo.yaml deleted file mode 100644 index 51a2c9d6..00000000 --- a/openapi/components/schemas/common/IbanAccountInfo.yaml +++ /dev/null @@ -1,23 +0,0 @@ -type: object -required: - - iban - - swiftBic - - accountType -properties: - accountType: - type: string - enum: - - IBAN - iban: - type: string - description: International Bank Account Number - example: DE89370400440532013000 - minLength: 15 - maxLength: 34 - swiftBic: - type: string - description: SWIFT/BIC code (8 or 11 characters) - example: DEUTDEFF - minLength: 8 - maxLength: 11 - pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ diff --git a/openapi/components/schemas/common/NgnAccountInfo.yaml b/openapi/components/schemas/common/NgnAccountInfo.yaml deleted file mode 100644 index cd9eb309..00000000 --- a/openapi/components/schemas/common/NgnAccountInfo.yaml +++ /dev/null @@ -1,21 +0,0 @@ -type: object -required: - - accountNumber - - bankName - - accountType -properties: - accountType: - type: string - enum: - - NGN_ACCOUNT - accountNumber: - type: string - description: Nigerian bank account number - example: '0123456789' - minLength: 10 - maxLength: 10 - pattern: ^[0-9]{10}$ - bankName: - type: string - description: Name of the bank - example: First Bank of Nigeria \ No newline at end of file diff --git a/openapi/components/schemas/common/PaymentAccountType.yaml b/openapi/components/schemas/common/PaymentAccountType.yaml deleted file mode 100644 index 1b5f6af5..00000000 --- a/openapi/components/schemas/common/PaymentAccountType.yaml +++ /dev/null @@ -1,16 +0,0 @@ -type: string -enum: - - CLABE - - US_ACCOUNT - - PIX - - IBAN - - UPI - - NGN_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET -description: Type of payment account or wallet -example: US_ACCOUNT diff --git a/openapi/components/schemas/common/PaymentBaseWalletInfo.yaml b/openapi/components/schemas/common/PaymentBaseWalletInfo.yaml deleted file mode 100644 index e2d3c2f6..00000000 --- a/openapi/components/schemas/common/PaymentBaseWalletInfo.yaml +++ /dev/null @@ -1,10 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./BaseWalletInfo.yaml - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC diff --git a/openapi/components/schemas/common/PaymentClabeAccountInfo.yaml b/openapi/components/schemas/common/PaymentClabeAccountInfo.yaml deleted file mode 100644 index 29f6d4a2..00000000 --- a/openapi/components/schemas/common/PaymentClabeAccountInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./ClabeAccountInfo.yaml - - type: object - required: - - reference - properties: - reference: - type: string - description: >- - Unique reference code that must be included with the payment to properly - credit it - example: UMA-Q12345-REF diff --git a/openapi/components/schemas/common/PaymentIbanAccountInfo.yaml b/openapi/components/schemas/common/PaymentIbanAccountInfo.yaml deleted file mode 100644 index d5c67fb1..00000000 --- a/openapi/components/schemas/common/PaymentIbanAccountInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./IbanAccountInfo.yaml - - type: object - required: - - reference - properties: - reference: - type: string - description: >- - Unique reference code that must be included with the payment to properly - credit it - example: UMA-Q12345-REF diff --git a/openapi/components/schemas/common/PaymentInstructions.yaml b/openapi/components/schemas/common/PaymentInstructions.yaml deleted file mode 100644 index 313d0cb1..00000000 --- a/openapi/components/schemas/common/PaymentInstructions.yaml +++ /dev/null @@ -1,52 +0,0 @@ -type: object -required: - - accountOrWalletInfo -properties: - instructionsNotes: - type: string - description: Additional human-readable instructions for making the payment - example: >- - Please ensure the reference code is included in the payment - memo/description field - isPlatformAccount: - type: boolean - description: Indicates whether the account is a platform account or a customer account. - example: true - accountOrWalletInfo: - oneOf: - - title: CLABE Account - $ref: ../common/PaymentClabeAccountInfo.yaml - - title: US Bank Account - $ref: ../common/PaymentUsAccountInfo.yaml - - title: PIX Account - $ref: ../common/PaymentPixAccountInfo.yaml - - title: IBAN Account - $ref: ../common/PaymentIbanAccountInfo.yaml - - title: UPI Account - $ref: ../common/PaymentUpiAccountInfo.yaml - - title: Spark Wallet - $ref: ../common/PaymentSparkWalletInfo.yaml - - title: Lightning Invoice - $ref: ../common/PaymentLightningInvoiceInfo.yaml - - title: Solana Wallet - $ref: ../common/PaymentSolanaWalletInfo.yaml - - title: Tron Wallet - $ref: ../common/PaymentTronWalletInfo.yaml - - title: Polygon Wallet - $ref: ../common/PaymentPolygonWalletInfo.yaml - - title: Base Wallet - $ref: ../common/PaymentBaseWalletInfo.yaml - discriminator: - propertyName: accountType - mapping: - CLABE: ../common/PaymentClabeAccountInfo.yaml - US_ACCOUNT: ../common/PaymentUsAccountInfo.yaml - PIX: ../common/PaymentPixAccountInfo.yaml - IBAN: ../common/PaymentIbanAccountInfo.yaml - UPI: ../common/PaymentUpiAccountInfo.yaml - SPARK_WALLET: ../common/PaymentSparkWalletInfo.yaml - LIGHTNING: ../common/PaymentLightningInvoiceInfo.yaml - SOLANA_WALLET: ../common/PaymentSolanaWalletInfo.yaml - TRON_WALLET: ../common/PaymentTronWalletInfo.yaml - POLYGON_WALLET: ../common/PaymentPolygonWalletInfo.yaml - BASE_WALLET: ../common/PaymentBaseWalletInfo.yaml diff --git a/openapi/components/schemas/common/PaymentLightningInvoiceInfo.yaml b/openapi/components/schemas/common/PaymentLightningInvoiceInfo.yaml deleted file mode 100644 index 2750adf3..00000000 --- a/openapi/components/schemas/common/PaymentLightningInvoiceInfo.yaml +++ /dev/null @@ -1,10 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - type: object - required: - - invoice - properties: - invoice: - type: string - description: Invoice for the payment - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs diff --git a/openapi/components/schemas/common/PaymentNgnAccountInfo.yaml b/openapi/components/schemas/common/PaymentNgnAccountInfo.yaml deleted file mode 100644 index b32b47bd..00000000 --- a/openapi/components/schemas/common/PaymentNgnAccountInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./NgnAccountInfo.yaml - - type: object - required: - - reference - properties: - reference: - type: string - description: >- - Unique reference code that must be included with the payment to properly - credit it - example: UMA-Q12345-REF diff --git a/openapi/components/schemas/common/PaymentPixAccountInfo.yaml b/openapi/components/schemas/common/PaymentPixAccountInfo.yaml deleted file mode 100644 index f8e0b0bc..00000000 --- a/openapi/components/schemas/common/PaymentPixAccountInfo.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./PixAccountInfo.yaml diff --git a/openapi/components/schemas/common/PaymentPolygonWalletInfo.yaml b/openapi/components/schemas/common/PaymentPolygonWalletInfo.yaml deleted file mode 100644 index 76f3b860..00000000 --- a/openapi/components/schemas/common/PaymentPolygonWalletInfo.yaml +++ /dev/null @@ -1,11 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./PolygonWalletInfo.yaml - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - \ No newline at end of file diff --git a/openapi/components/schemas/common/PaymentSolanaWalletInfo.yaml b/openapi/components/schemas/common/PaymentSolanaWalletInfo.yaml deleted file mode 100644 index 5e674abf..00000000 --- a/openapi/components/schemas/common/PaymentSolanaWalletInfo.yaml +++ /dev/null @@ -1,11 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./SolanaWalletInfo.yaml - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: - - USDC - - USDT diff --git a/openapi/components/schemas/common/PaymentSparkWalletInfo.yaml b/openapi/components/schemas/common/PaymentSparkWalletInfo.yaml deleted file mode 100644 index b4a0b247..00000000 --- a/openapi/components/schemas/common/PaymentSparkWalletInfo.yaml +++ /dev/null @@ -1,17 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./SparkWalletInfo.yaml - - type: object - required: - - assetType - properties: - assetType: - type: string - description: Type of asset - enum: - - BTC - - USDB - invoice: - type: string - description: Invoice for the payment - example: sparkrt1pgss8ter0fhc4c220f3zftmpz49h8wqte8eg3m5zkrraplgc048jucgszg3ssqgjzqqekv73mmh842yj7drsjwh7t7tz5zt8wf5kghm5v4ehggszppjp5s80cg3qjdzc55g2567tn3lj705hdsr577tg8ah795mlnt6807y657qhkmgfkf9w75p4wz3l8vhua85zdn6ryj32zuj0p00pv2l5z4u47mw6h4s diff --git a/openapi/components/schemas/common/PaymentTronWalletInfo.yaml b/openapi/components/schemas/common/PaymentTronWalletInfo.yaml deleted file mode 100644 index 1dbebb9d..00000000 --- a/openapi/components/schemas/common/PaymentTronWalletInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./TronWalletInfo.yaml - - type: object - properties: - assetType: - type: string - description: Type of asset - enum: [USDT] diff --git a/openapi/components/schemas/common/PaymentUpiAccountInfo.yaml b/openapi/components/schemas/common/PaymentUpiAccountInfo.yaml deleted file mode 100644 index af4b9900..00000000 --- a/openapi/components/schemas/common/PaymentUpiAccountInfo.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./UpiAccountInfo.yaml diff --git a/openapi/components/schemas/common/PaymentUsAccountInfo.yaml b/openapi/components/schemas/common/PaymentUsAccountInfo.yaml deleted file mode 100644 index 526d5d06..00000000 --- a/openapi/components/schemas/common/PaymentUsAccountInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -allOf: - - $ref: ./BasePaymentAccountInfo.yaml - - $ref: ./UsAccountInfo.yaml - - type: object - required: - - reference - properties: - reference: - type: string - description: >- - Unique reference code that must be included with the payment to properly - credit it - example: UMA-Q12345-REF diff --git a/openapi/components/schemas/common/PhpAccountInfo.yaml b/openapi/components/schemas/common/PhpAccountInfo.yaml deleted file mode 100644 index 9b01eda6..00000000 --- a/openapi/components/schemas/common/PhpAccountInfo.yaml +++ /dev/null @@ -1,18 +0,0 @@ -type: object -required: - - bankName - - accountNumber - - accountType -properties: - accountType: - type: string - enum: - - PHP_ACCOUNT - bankName: - type: string - description: Name of the beneficiary's bank - example: BDO Unibank - accountNumber: - type: string - description: Bank account number - example: '001234567890' diff --git a/openapi/components/schemas/common/PixAccountInfo.yaml b/openapi/components/schemas/common/PixAccountInfo.yaml deleted file mode 100644 index fb37639a..00000000 --- a/openapi/components/schemas/common/PixAccountInfo.yaml +++ /dev/null @@ -1,29 +0,0 @@ -type: object -required: - - pixKey - - pixKeyType - - taxId - - accountType -properties: - accountType: - type: string - enum: - - PIX - pixKey: - type: string - description: PIX key for Brazilian instant payments - example: '55119876543210' - pixKeyType: - type: string - enum: - - CPF - - CNPJ - - EMAIL - - PHONE - - RANDOM - description: Type of PIX key being used - example: PHONE - taxId: - type: string - description: Tax ID of the account holder - example: '1234567890' diff --git a/openapi/components/schemas/common/PolygonWalletInfo.yaml b/openapi/components/schemas/common/PolygonWalletInfo.yaml deleted file mode 100644 index 0b52715f..00000000 --- a/openapi/components/schemas/common/PolygonWalletInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - address - - accountType -properties: - accountType: - type: string - enum: - - POLYGON_WALLET - address: - type: string - description: Polygon eth wallet address - example: '0xAbCDEF1234567890aBCdEf1234567890ABcDef12' diff --git a/openapi/components/schemas/common/ReconciliationInstructions.yaml b/openapi/components/schemas/common/ReconciliationInstructions.yaml deleted file mode 100644 index 49411ab2..00000000 --- a/openapi/components/schemas/common/ReconciliationInstructions.yaml +++ /dev/null @@ -1,10 +0,0 @@ -type: object -required: - - reference -properties: - reference: - type: string - description: >- - Unique reference code that must be included with the payment to match it - with the correct incoming transaction - example: UMA-Q12345-REF diff --git a/openapi/components/schemas/common/Refund.yaml b/openapi/components/schemas/common/Refund.yaml deleted file mode 100644 index a3603533..00000000 --- a/openapi/components/schemas/common/Refund.yaml +++ /dev/null @@ -1,19 +0,0 @@ -type: object -required: - - reference - - initiatedAt -properties: - reference: - type: string - description: The unique reference code of the refund - example: UMA-Q12345-REFUND - initiatedAt: - type: string - format: date-time - description: When the refund was initiated - example: '2025-08-15T14:30:00Z' - settledAt: - type: string - format: date-time - description: When the refund was or will be settled - example: '2025-08-15T14:30:00Z' diff --git a/openapi/components/schemas/common/SgdAccountInfo.yaml b/openapi/components/schemas/common/SgdAccountInfo.yaml deleted file mode 100644 index 1821b632..00000000 --- a/openapi/components/schemas/common/SgdAccountInfo.yaml +++ /dev/null @@ -1,26 +0,0 @@ -type: object -required: - - bankName - - swiftCode - - accountNumber - - accountType -properties: - accountType: - type: string - enum: - - SGD_ACCOUNT - bankName: - type: string - description: Name of the beneficiary's bank - example: DBS Bank Ltd - swiftCode: - type: string - description: SWIFT/BIC code (8 or 11 characters) - example: DBSSSGSG - minLength: 8 - maxLength: 11 - pattern: ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$ - accountNumber: - type: string - description: Bank account number - example: '0123456789' diff --git a/openapi/components/schemas/common/SolanaWalletInfo.yaml b/openapi/components/schemas/common/SolanaWalletInfo.yaml deleted file mode 100644 index a2ff50c4..00000000 --- a/openapi/components/schemas/common/SolanaWalletInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - address - - accountType -properties: - accountType: - type: string - enum: - - SOLANA_WALLET - address: - type: string - description: Solana wallet address - example: '4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg' diff --git a/openapi/components/schemas/common/SparkWalletInfo.yaml b/openapi/components/schemas/common/SparkWalletInfo.yaml deleted file mode 100644 index 07065326..00000000 --- a/openapi/components/schemas/common/SparkWalletInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - address - - accountType -properties: - accountType: - type: string - enum: - - SPARK_WALLET - address: - type: string - description: Spark wallet address - example: 'spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu' diff --git a/openapi/components/schemas/common/TronWalletInfo.yaml b/openapi/components/schemas/common/TronWalletInfo.yaml deleted file mode 100644 index 3bffa8c0..00000000 --- a/openapi/components/schemas/common/TronWalletInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - address - - accountType -properties: - accountType: - type: string - enum: - - TRON_WALLET - address: - type: string - description: Tron wallet address - example: 'TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL' diff --git a/openapi/components/schemas/common/UpiAccountInfo.yaml b/openapi/components/schemas/common/UpiAccountInfo.yaml deleted file mode 100644 index dc579b61..00000000 --- a/openapi/components/schemas/common/UpiAccountInfo.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - vpa - - accountType -properties: - accountType: - type: string - enum: - - UPI - vpa: - type: string - description: Virtual Payment Address for UPI payments - example: somecustomers@okbank diff --git a/openapi/components/schemas/common/UsAccountInfo.yaml b/openapi/components/schemas/common/UsAccountInfo.yaml deleted file mode 100644 index e5673516..00000000 --- a/openapi/components/schemas/common/UsAccountInfo.yaml +++ /dev/null @@ -1,33 +0,0 @@ -type: object -required: - - accountNumber - - routingNumber - - accountCategory - - accountType -properties: - accountType: - type: string - enum: - - US_ACCOUNT - accountNumber: - type: string - description: US bank account number - example: '123456789' - routingNumber: - type: string - description: ACH routing number (9 digits) - example: '987654321' - minLength: 9 - maxLength: 9 - pattern: ^[0-9]{9}$ - accountCategory: - type: string - enum: - - CHECKING - - SAVINGS - description: Type of account (checking or savings) - example: CHECKING - bankName: - type: string - description: Name of the bank - example: Chase Bank diff --git a/openapi/components/schemas/config/PlatformConfig.yaml b/openapi/components/schemas/config/PlatformConfig.yaml deleted file mode 100644 index 5cbc0235..00000000 --- a/openapi/components/schemas/config/PlatformConfig.yaml +++ /dev/null @@ -1,46 +0,0 @@ -type: object -properties: - id: - type: string - description: System-generated unique identifier - readOnly: true - example: PlatformConfig:019542f5-b3e7-1d02-0000-000000000003 - umaDomain: - type: string - description: UMA domain for this platform - example: platform.uma.domain - proxyUmaSubdomain: - type: string - description: The subdomain that incoming requests will be proxied to - example: platform - webhookEndpoint: - type: string - description: URL where webhook notifications will be sent - example: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - type: array - items: - $ref: ./PlatformCurrencyConfig.yaml - description: | - List of currencies supported by the platform. This is what the platform's - customers are able to hold, send, and receive. - isRegulatedFinancialInstitution: - type: boolean - description: | - Whether the platform is a regulated financial institution. This is used to - determine if the platform's customers must be KYC/KYB'd by Lightspark via - the KYC link flow. This can only be set by Lightspark during platform - creation. - example: false - createdAt: - type: string - format: date-time - description: Creation timestamp - readOnly: true - example: '2025-06-15T12:30:45Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - readOnly: true - example: '2025-06-15T12:30:45Z' diff --git a/openapi/components/schemas/config/PlatformCurrencyConfig.yaml b/openapi/components/schemas/config/PlatformCurrencyConfig.yaml deleted file mode 100644 index a06aa2ab..00000000 --- a/openapi/components/schemas/config/PlatformCurrencyConfig.yaml +++ /dev/null @@ -1,73 +0,0 @@ -type: object -properties: - currencyCode: - type: string - description: Three-letter currency code (ISO 4217) - example: USD - minAmount: - type: integer - format: int64 - description: Minimum amount that can be sent in the smallest unit of this currency - minimum: 0 - example: 100 - maxAmount: - type: integer - format: int64 - description: Maximum amount that can be sent in the smallest unit of this currency - minimum: 0 - example: 1000000 - requiredCounterpartyFields: - type: array - items: - $ref: ../common/CounterpartyFieldDefinition.yaml - description: >- - List of fields which the platform requires from the counterparty - institutions about counterparty customers. Platforms can set mandatory to - false if the platform does not require the field, but would like to have - it available. Some fields may be required by the underlying UMA provider. - example: - - name: FULL_NAME - mandatory: true - - name: BIRTH_DATE - mandatory: true - - name: NATIONALITY - mandatory: true - providerRequiredCustomerFields: - type: array - items: - $ref: ../customers/CustomerInfoFieldName.yaml - description: >- - List of customer info field names that are required by the underlying UMA - provider when creating a customer for this currency. These fields must be - supplied when creating or updating a customer if this currency is intended to - be used by that customer. If no fields are required, this field is omitted. - readOnly: true - example: - - NATIONALITY - - BIRTH_DATE - providerRequiredCounterpartyCustomerFields: - type: array - items: - $ref: ../customers/CustomerInfoFieldName.yaml - description: >- - List of fields that are required by the underlying UMA provider for this - currency. If the counterparty does not provide these fields, quote - requests will fail. - readOnly: true - example: - - FULL_NAME - - COUNTRY_OF_RESIDENCE - enabledTransactionTypes: - type: array - items: - $ref: ../transactions/TransactionType.yaml - description: List of transaction types that are enabled for this currency. - example: - - OUTGOING - - INCOMING -required: - - currencyCode - - minAmount - - maxAmount - - requiredCounterpartyFields - - enabledTransactionTypes diff --git a/openapi/components/schemas/customers/BulkCustomerImportErrorEntry.yaml b/openapi/components/schemas/customers/BulkCustomerImportErrorEntry.yaml deleted file mode 100644 index 79bfb0bd..00000000 --- a/openapi/components/schemas/customers/BulkCustomerImportErrorEntry.yaml +++ /dev/null @@ -1,11 +0,0 @@ -allOf: - - $ref: ../common/GridError.yaml - - type: object - description: Error information for a failed bulk import entry - required: - - correlationId - properties: - correlationId: - type: string - description: Platform customer ID or row number for the failed entry - example: biz456 diff --git a/openapi/components/schemas/customers/BulkCustomerImportJob.yaml b/openapi/components/schemas/customers/BulkCustomerImportJob.yaml deleted file mode 100644 index 1c74dc1e..00000000 --- a/openapi/components/schemas/customers/BulkCustomerImportJob.yaml +++ /dev/null @@ -1,55 +0,0 @@ -type: object -required: - - jobId - - status - - progress -properties: - jobId: - type: string - description: Unique identifier for the bulk import job - example: Job:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - description: Current status of the job - example: PROCESSING - progress: - type: object - required: - - total - - processed - - successful - - failed - properties: - total: - type: integer - description: Total number of customers to process - example: 5000 - processed: - type: integer - description: Number of customers processed so far - example: 2500 - successful: - type: integer - description: Number of customers successfully created - example: 2450 - failed: - type: integer - description: Number of customers that failed to create - example: 50 - errors: - type: array - description: Detailed error information for failed entries - items: - $ref: BulkCustomerImportErrorEntry.yaml - completedAt: - type: string - format: date-time - description: >- - Timestamp when the job completed (only present for COMPLETED or FAILED - status) - example: '2025-08-15T14:32:00Z' diff --git a/openapi/components/schemas/customers/BulkUploadWebhookRequest.yaml b/openapi/components/schemas/customers/BulkUploadWebhookRequest.yaml deleted file mode 100644 index 712b43bc..00000000 --- a/openapi/components/schemas/customers/BulkUploadWebhookRequest.yaml +++ /dev/null @@ -1,12 +0,0 @@ -allOf: - - $ref: ../webhooks/BaseWebhook.yaml - - type: object - required: - - bulkCustomerImportJob - properties: - bulkCustomerImportJob: - $ref: ./BulkCustomerImportJob.yaml - type: - $ref: ../webhooks/WebhookType.yaml - description: Type of webhook event - example: BULK_UPLOAD diff --git a/openapi/components/schemas/customers/BusinessCustomer.yaml b/openapi/components/schemas/customers/BusinessCustomer.yaml deleted file mode 100644 index 0d72cf6a..00000000 --- a/openapi/components/schemas/customers/BusinessCustomer.yaml +++ /dev/null @@ -1,7 +0,0 @@ -allOf: - - $ref: ./Customer.yaml - - $ref: ./BusinessCustomerFields.yaml - - type: object - properties: - businessInfo: - $ref: ./BusinessInfo.yaml diff --git a/openapi/components/schemas/customers/BusinessCustomerCreateRequest.yaml b/openapi/components/schemas/customers/BusinessCustomerCreateRequest.yaml deleted file mode 100644 index 8ad56cc2..00000000 --- a/openapi/components/schemas/customers/BusinessCustomerCreateRequest.yaml +++ /dev/null @@ -1,7 +0,0 @@ -allOf: - - $ref: ./CustomerCreateRequest.yaml - - $ref: ./BusinessCustomerFields.yaml - - type: object - properties: - businessInfo: - $ref: ./BusinessInfo.yaml diff --git a/openapi/components/schemas/customers/BusinessCustomerFields.yaml b/openapi/components/schemas/customers/BusinessCustomerFields.yaml deleted file mode 100644 index 998bf832..00000000 --- a/openapi/components/schemas/customers/BusinessCustomerFields.yaml +++ /dev/null @@ -1,16 +0,0 @@ -type: object -required: - - customerType -properties: - customerType: - type: string - enum: - - BUSINESS - address: - $ref: ../common/Address.yaml - businessInfo: - $ref: ./BusinessInfoUpdate.yaml - beneficialOwners: - type: array - items: - $ref: ./UltimateBeneficialOwner.yaml diff --git a/openapi/components/schemas/customers/BusinessCustomerUpdateRequest.yaml b/openapi/components/schemas/customers/BusinessCustomerUpdateRequest.yaml deleted file mode 100644 index 6d059073..00000000 --- a/openapi/components/schemas/customers/BusinessCustomerUpdateRequest.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allOf: - - $ref: ./CustomerUpdateRequest.yaml - - $ref: ./BusinessCustomerFields.yaml diff --git a/openapi/components/schemas/customers/BusinessInfo.yaml b/openapi/components/schemas/customers/BusinessInfo.yaml deleted file mode 100644 index fe6d4294..00000000 --- a/openapi/components/schemas/customers/BusinessInfo.yaml +++ /dev/null @@ -1,17 +0,0 @@ -type: object -description: Additional information required for business entities -required: - - legalName -properties: - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 diff --git a/openapi/components/schemas/customers/BusinessInfoUpdate.yaml b/openapi/components/schemas/customers/BusinessInfoUpdate.yaml deleted file mode 100644 index 102e4280..00000000 --- a/openapi/components/schemas/customers/BusinessInfoUpdate.yaml +++ /dev/null @@ -1,15 +0,0 @@ -type: object -description: Additional information for business entities -properties: - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 diff --git a/openapi/components/schemas/customers/Customer.yaml b/openapi/components/schemas/customers/Customer.yaml deleted file mode 100644 index 2a0b6fa6..00000000 --- a/openapi/components/schemas/customers/Customer.yaml +++ /dev/null @@ -1,47 +0,0 @@ -type: object -required: - - umaAddress - - platformCustomerId - - customerType -properties: - id: - type: string - description: System-generated unique identifier - readOnly: true - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: Platform-specific customer identifier - example: 9f84e0c2a72c4fa - customerType: - $ref: ./CustomerType.yaml - kycStatus: - $ref: ./KycStatus.yaml - umaAddress: - type: string - description: >- - Full UMA address (always present in responses, even if system-generated). This is an optional identifier - to route payments to the customer. - example: $john.doe@uma.domain.com - createdAt: - type: string - format: date-time - description: Creation timestamp - readOnly: true - example: '2025-07-21T17:32:28Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - readOnly: true - example: '2025-07-21T17:32:28Z' - isDeleted: - type: boolean - description: Whether the customer is marked as deleted - example: false - readOnly: true -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ./IndividualCustomer.yaml - BUSINESS: ./BusinessCustomer.yaml diff --git a/openapi/components/schemas/customers/CustomerCreateRequest.yaml b/openapi/components/schemas/customers/CustomerCreateRequest.yaml deleted file mode 100644 index a5f17821..00000000 --- a/openapi/components/schemas/customers/CustomerCreateRequest.yaml +++ /dev/null @@ -1,23 +0,0 @@ -type: object -required: - - customerType - - platformCustomerId -properties: - platformCustomerId: - type: string - description: Platform-specific customer identifier. If not provided, one will be generated by the system. - example: 9f84e0c2a72c4fa - customerType: - $ref: ./CustomerType.yaml - umaAddress: - type: string - description: >- - Optional UMA address identifier. If not provided during customer creation, one will be generated by the system. - If provided during customer update, the UMA address will be updated to the provided value. This is an optional identifier to route payments to the customer. - This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ./IndividualCustomerCreateRequest.yaml - BUSINESS: ./BusinessCustomerCreateRequest.yaml diff --git a/openapi/components/schemas/customers/CustomerCreateRequestOneOf.yaml b/openapi/components/schemas/customers/CustomerCreateRequestOneOf.yaml deleted file mode 100644 index 53e9c955..00000000 --- a/openapi/components/schemas/customers/CustomerCreateRequestOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Individual Customer Create Request - $ref: ./IndividualCustomerCreateRequest.yaml - - title: Business Customer Create Request - $ref: ./BusinessCustomerCreateRequest.yaml -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ./IndividualCustomerCreateRequest.yaml - BUSINESS: ./BusinessCustomerCreateRequest.yaml diff --git a/openapi/components/schemas/customers/CustomerInfoFieldName.yaml b/openapi/components/schemas/customers/CustomerInfoFieldName.yaml deleted file mode 100644 index c7b89537..00000000 --- a/openapi/components/schemas/customers/CustomerInfoFieldName.yaml +++ /dev/null @@ -1,22 +0,0 @@ -type: string -enum: - - FULL_NAME - - BIRTH_DATE - - NATIONALITY - - PHONE_NUMBER - - EMAIL - - POSTAL_ADDRESS - - TAX_ID - - REGISTRATION_NUMBER - - USER_TYPE - - COUNTRY_OF_RESIDENCE - - ACCOUNT_IDENTIFIER - - FI_LEGAL_ENTITY_NAME - - FI_ADDRESS - - PURPOSE_OF_PAYMENT - - ULTIMATE_INSTITUTION_COUNTRY - - IDENTIFIER -description: >- - Name of a type of field containing info about a platform's customer or - counterparty customer. -example: FULL_NAME diff --git a/openapi/components/schemas/customers/CustomerOneOf.yaml b/openapi/components/schemas/customers/CustomerOneOf.yaml deleted file mode 100644 index c274c9ec..00000000 --- a/openapi/components/schemas/customers/CustomerOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Individual Customer - $ref: ./IndividualCustomer.yaml - - title: Business Customer - $ref: ./BusinessCustomer.yaml -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ./IndividualCustomer.yaml - BUSINESS: ./BusinessCustomer.yaml diff --git a/openapi/components/schemas/customers/CustomerType.yaml b/openapi/components/schemas/customers/CustomerType.yaml deleted file mode 100644 index 8aaed521..00000000 --- a/openapi/components/schemas/customers/CustomerType.yaml +++ /dev/null @@ -1,6 +0,0 @@ -type: string -enum: - - INDIVIDUAL - - BUSINESS -description: Whether the customer is an individual or a business entity -example: INDIVIDUAL diff --git a/openapi/components/schemas/customers/CustomerUpdateRequest.yaml b/openapi/components/schemas/customers/CustomerUpdateRequest.yaml deleted file mode 100644 index 68585c67..00000000 --- a/openapi/components/schemas/customers/CustomerUpdateRequest.yaml +++ /dev/null @@ -1,17 +0,0 @@ -type: object -required: - - customerType -properties: - customerType: - $ref: ./CustomerType.yaml - umaAddress: - type: string - description: >- - Optional UMA address identifier. If provided, the customer's UMA address will be updated. - This is an optional identifier to route payments to the customer. - example: $john.doe@uma.domain.com -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ./IndividualCustomerUpdateRequest.yaml - BUSINESS: ./BusinessCustomerUpdateRequest.yaml diff --git a/openapi/components/schemas/customers/CustomerUpdateRequestOneOf.yaml b/openapi/components/schemas/customers/CustomerUpdateRequestOneOf.yaml deleted file mode 100644 index 3574653e..00000000 --- a/openapi/components/schemas/customers/CustomerUpdateRequestOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Individual Customer Update Request - $ref: ./IndividualCustomerUpdateRequest.yaml - - title: Business Customer Update Request - $ref: ./BusinessCustomerUpdateRequest.yaml -discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ./IndividualCustomerUpdateRequest.yaml - BUSINESS: ./BusinessCustomerUpdateRequest.yaml diff --git a/openapi/components/schemas/customers/IndividualCustomer.yaml b/openapi/components/schemas/customers/IndividualCustomer.yaml deleted file mode 100644 index 8c878765..00000000 --- a/openapi/components/schemas/customers/IndividualCustomer.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allOf: - - $ref: ./Customer.yaml - - $ref: ./IndividualCustomerFields.yaml diff --git a/openapi/components/schemas/customers/IndividualCustomerCreateRequest.yaml b/openapi/components/schemas/customers/IndividualCustomerCreateRequest.yaml deleted file mode 100644 index 2e64cfab..00000000 --- a/openapi/components/schemas/customers/IndividualCustomerCreateRequest.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allOf: - - $ref: ./CustomerCreateRequest.yaml - - $ref: ./IndividualCustomerFields.yaml diff --git a/openapi/components/schemas/customers/IndividualCustomerFields.yaml b/openapi/components/schemas/customers/IndividualCustomerFields.yaml deleted file mode 100644 index 95616fef..00000000 --- a/openapi/components/schemas/customers/IndividualCustomerFields.yaml +++ /dev/null @@ -1,23 +0,0 @@ -type: object -required: - - customerType -properties: - customerType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: Individual's full name - example: John Michael Doe - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - address: - $ref: ../common/Address.yaml diff --git a/openapi/components/schemas/customers/IndividualCustomerUpdateRequest.yaml b/openapi/components/schemas/customers/IndividualCustomerUpdateRequest.yaml deleted file mode 100644 index e5d536f9..00000000 --- a/openapi/components/schemas/customers/IndividualCustomerUpdateRequest.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allOf: - - $ref: ./CustomerUpdateRequest.yaml - - $ref: ./IndividualCustomerFields.yaml diff --git a/openapi/components/schemas/customers/InternalAccount.yaml b/openapi/components/schemas/customers/InternalAccount.yaml deleted file mode 100644 index bb7aaa59..00000000 --- a/openapi/components/schemas/customers/InternalAccount.yaml +++ /dev/null @@ -1,34 +0,0 @@ -type: object -required: - - id - - balance - - fundingPaymentInstructions - - createdAt - - updatedAt -properties: - id: - type: string - description: The ID of the internal account - example: InternalAccount:12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - customerId: - type: string - description: The ID of the customer associated with the internal account. If this field is empty, the internal account belongs to the platform. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - balance: - $ref: ../common/CurrencyAmount.yaml - fundingPaymentInstructions: - type: array - description: Payment instructions for funding the account - items: - $ref: ../common/PaymentInstructions.yaml - createdAt: - type: string - format: date-time - description: Timestamp when the internal account was created - example: 2025-10-03T12:30:00Z - updatedAt: - type: string - format: date-time - description: Timestamp when the internal account was last updated - example: 2025-10-03T12:30:00Z - diff --git a/openapi/components/schemas/customers/KycStatus.yaml b/openapi/components/schemas/customers/KycStatus.yaml deleted file mode 100644 index f6edc4c6..00000000 --- a/openapi/components/schemas/customers/KycStatus.yaml +++ /dev/null @@ -1,11 +0,0 @@ -type: string -enum: - - APPROVED - - REJECTED - - PENDING_REVIEW - - EXPIRED - - CANCELED - - MANUALLY_APPROVED - - MANUALLY_REJECTED -description: The current KYC status of a customer -example: APPROVED diff --git a/openapi/components/schemas/customers/UltimateBeneficialOwner.yaml b/openapi/components/schemas/customers/UltimateBeneficialOwner.yaml deleted file mode 100644 index 4cee1fe4..00000000 --- a/openapi/components/schemas/customers/UltimateBeneficialOwner.yaml +++ /dev/null @@ -1,53 +0,0 @@ -type: object -required: - - fullName - - individualType -properties: - fullName: - type: string - description: Individual's full name - example: John Michael Doe - emailAddress: - type: string - format: email - description: Email address of the individual - example: example@test.com - phoneNumber: - type: string - description: Phone number of the individual in E.164 format - example: '+5555555555' - pattern: '^\+[1-9]\d{1,14}$' - taxId: - type: string - description: Tax identification number of the individual. This could be a Social Security Number (SSN) for US individuals, Tax Identification Number (TIN) for non-US individuals, or a Passport Number. - example: EIN-987654321 - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US - address: - $ref: ../common/Address.yaml - individualType: - type: string - enum: - - DIRECTOR - - CONTROL_PERSON - - BUSINESS_POINT_OF_CONTACT - - TRUSTEE - - SETTLOR - - GENERAL_PARTNER - description: Type of individual in the corporation - example: DIRECTOR - percentageOwnership: - type: number - description: Percent of ownership when individual type is beneficial owner - example: 25 - title: - type: string - description: Title at company - example: CEO, COO, President diff --git a/openapi/components/schemas/errors/Error400.yaml b/openapi/components/schemas/errors/Error400.yaml deleted file mode 100644 index 804857b9..00000000 --- a/openapi/components/schemas/errors/Error400.yaml +++ /dev/null @@ -1,78 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 400 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | INVALID_INPUT | Invalid input provided | - | MISSING_MANDATORY_USER_INFO | Required customer information is missing | - | INVITATION_ALREADY_CLAIMED | Invitation has already been claimed | - | INVITATIONS_NOT_CONFIGURED | Invitations are not configured | - | INVALID_UMA_ADDRESS | UMA address format is invalid | - | INVITATION_CANCELLED | Invitation has been cancelled | - | QUOTE_REQUEST_FAILED | An issue occurred during the quote process; this is retryable | - | INVALID_PAYREQ_RESPONSE | Counterparty Payreq response was invalid | - | INVALID_RECEIVER | Receiver is invalid | - | PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq response | - | CERT_CHAIN_INVALID | Counterparty certificate chain is invalid | - | CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired | - | INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid | - | MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA parameters are missing | - | SENDER_NOT_ACCEPTED | Sender is not accepted | - | AMOUNT_OUT_OF_RANGE | Amount is out of range | - | INVALID_CURRENCY | Currency is invalid | - | INVALID_TIMESTAMP | Timestamp is invalid | - | INVALID_NONCE | Nonce is invalid | - | INVALID_REQUEST_FORMAT | Request format is invalid | - | INVALID_BANK_ACCOUNT | Bank account is invalid | - | SELF_PAYMENT | Self payment not allowed | - | LOOKUP_REQUEST_FAILED | Lookup request failed | - | PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response | - | INVALID_AMOUNT | Amount is invalid | - | WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set | - | WEBHOOK_DELIVERY_ERROR | Webhook delivery error | - enum: - - INVALID_INPUT - - MISSING_MANDATORY_USER_INFO - - INVITATION_ALREADY_CLAIMED - - INVITATIONS_NOT_CONFIGURED - - INVALID_UMA_ADDRESS - - INVITATION_CANCELLED - - QUOTE_REQUEST_FAILED - - INVALID_PAYREQ_RESPONSE - - INVALID_RECEIVER - - PARSE_PAYREQ_RESPONSE_ERROR - - CERT_CHAIN_INVALID - - CERT_CHAIN_EXPIRED - - INVALID_PUBKEY_FORMAT - - MISSING_REQUIRED_UMA_PARAMETERS - - SENDER_NOT_ACCEPTED - - AMOUNT_OUT_OF_RANGE - - INVALID_CURRENCY - - INVALID_TIMESTAMP - - INVALID_NONCE - - INVALID_REQUEST_FORMAT - - INVALID_BANK_ACCOUNT - - SELF_PAYMENT - - LOOKUP_REQUEST_FAILED - - PARSE_LNURLP_RESPONSE_ERROR - - INVALID_AMOUNT - - WEBHOOK_ENDPOINT_NOT_SET - - WEBHOOK_DELIVERY_ERROR - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error401.yaml b/openapi/components/schemas/errors/Error401.yaml deleted file mode 100644 index d92fb3b7..00000000 --- a/openapi/components/schemas/errors/Error401.yaml +++ /dev/null @@ -1,28 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 401 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNAUTHORIZED | Issue with API credentials | - | INVALID_SIGNATURE | Signature header is invalid | - enum: - - UNAUTHORIZED - - INVALID_SIGNATURE - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error403.yaml b/openapi/components/schemas/errors/Error403.yaml deleted file mode 100644 index c07ddfb4..00000000 --- a/openapi/components/schemas/errors/Error403.yaml +++ /dev/null @@ -1,32 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 403 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | FORBIDDEN | Insufficient permissions | - | USER_NOT_READY | Customer exists but is not ready for operation | - | COUNTERPARTY_NOT_ALLOWED | Counterparty has not been enabled for your account | - | VELOCITY_LIMIT_EXCEEDED | Counterparty has exceeded velocity limits | - enum: - - FORBIDDEN - - USER_NOT_READY - - COUNTERPARTY_NOT_ALLOWED - - VELOCITY_LIMIT_EXCEEDED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error404.yaml b/openapi/components/schemas/errors/Error404.yaml deleted file mode 100644 index d53ff414..00000000 --- a/openapi/components/schemas/errors/Error404.yaml +++ /dev/null @@ -1,40 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 404 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | TRANSACTION_NOT_FOUND | Transaction not found | - | INVITATION_NOT_FOUND | Invitation not found | - | USER_NOT_FOUND | Customer not found | - | QUOTE_NOT_FOUND | Quote not found | - | LOOKUP_REQUEST_NOT_FOUND | Lookup request not found | - | TOKEN_NOT_FOUND | Token not found | - | BULK_UPLOAD_JOB_NOT_FOUND | Bulk upload job not found | - | REFERENCE_NOT_FOUND | Reference not found | - enum: - - TRANSACTION_NOT_FOUND - - INVITATION_NOT_FOUND - - USER_NOT_FOUND - - QUOTE_NOT_FOUND - - LOOKUP_REQUEST_NOT_FOUND - - TOKEN_NOT_FOUND - - BULK_UPLOAD_JOB_NOT_FOUND - - REFERENCE_NOT_FOUND - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error409.yaml b/openapi/components/schemas/errors/Error409.yaml deleted file mode 100644 index b033a67f..00000000 --- a/openapi/components/schemas/errors/Error409.yaml +++ /dev/null @@ -1,28 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 409 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | - | UMA_ADDRESS_EXISTS | UMA address already exists | - enum: - - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - - UMA_ADDRESS_EXISTS - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error410.yaml b/openapi/components/schemas/errors/Error410.yaml deleted file mode 100644 index 57d4fee0..00000000 --- a/openapi/components/schemas/errors/Error410.yaml +++ /dev/null @@ -1,26 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 410 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | CUSTOMER_DELETED | Customer has been permanently deleted | - enum: - - CUSTOMER_DELETED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error412.yaml b/openapi/components/schemas/errors/Error412.yaml deleted file mode 100644 index 74edf256..00000000 --- a/openapi/components/schemas/errors/Error412.yaml +++ /dev/null @@ -1,25 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 412 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNSUPPORTED_UMA_VERSION | Counterparty doesn't support the Grid UMA version | - enum: [UNSUPPORTED_UMA_VERSION] - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error424.yaml b/openapi/components/schemas/errors/Error424.yaml deleted file mode 100644 index 1a9b5a4a..00000000 --- a/openapi/components/schemas/errors/Error424.yaml +++ /dev/null @@ -1,32 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 424 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | PAYREQ_REQUEST_FAILED | Payment request failed | - | COUNTERPARTY_PUBKEY_FETCH_ERROR | Error fetching counterparty public key | - | NO_COMPATIBLE_UMA_VERSION | No compatible UMA version | - | LNURLP_REQUEST_FAILED | LNURLP request failed | - enum: - - PAYREQ_REQUEST_FAILED - - COUNTERPARTY_PUBKEY_FETCH_ERROR - - NO_COMPATIBLE_UMA_VERSION - - LNURLP_REQUEST_FAILED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error500.yaml b/openapi/components/schemas/errors/Error500.yaml deleted file mode 100644 index 50be5ad6..00000000 --- a/openapi/components/schemas/errors/Error500.yaml +++ /dev/null @@ -1,28 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 500 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | GRID_SWITCH_ERROR | Grid switch error | - | INTERNAL_ERROR | Internal server or UMA error | - enum: - - GRID_SWITCH_ERROR - - INTERNAL_ERROR - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/errors/Error501.yaml b/openapi/components/schemas/errors/Error501.yaml deleted file mode 100644 index 7a0b797b..00000000 --- a/openapi/components/schemas/errors/Error501.yaml +++ /dev/null @@ -1,28 +0,0 @@ -type: object -required: - - message - - status - - code -properties: - status: - type: integer - enum: - - 501 - description: HTTP status code - code: - type: string - description: | - | Error Code | Description | - |------------|-------------| - | UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY | Unrecognized mandatory payee data key | - | NOT_IMPLEMENTED | Feature not implemented | - enum: - - UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY - - NOT_IMPLEMENTED - message: - type: string - description: Error message - details: - type: object - description: Additional error details - additionalProperties: true diff --git a/openapi/components/schemas/external_accounts/BaseBeneficiary.yaml b/openapi/components/schemas/external_accounts/BaseBeneficiary.yaml deleted file mode 100644 index 27bd7e85..00000000 --- a/openapi/components/schemas/external_accounts/BaseBeneficiary.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: object -required: - - beneficiaryType -properties: - beneficiaryType: - $ref: ./BeneficiaryType.yaml - address: - $ref: ../common/Address.yaml -discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: ./IndividualBeneficiary.yaml - BUSINESS: ./BusinessBeneficiary.yaml diff --git a/openapi/components/schemas/external_accounts/BaseExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/BaseExternalAccountInfo.yaml deleted file mode 100644 index ea40286e..00000000 --- a/openapi/components/schemas/external_accounts/BaseExternalAccountInfo.yaml +++ /dev/null @@ -1,25 +0,0 @@ -type: object -required: - - accountType -properties: - accountType: - $ref: ./ExternalAccountType.yaml -discriminator: - propertyName: accountType - mapping: - US_ACCOUNT: ./UsAccountExternalAccountInfo.yaml - CLABE: ./ClabeAccountExternalAccountInfo.yaml - PIX: ./PixAccountExternalAccountInfo.yaml - IBAN: ./IbanAccountExternalAccountInfo.yaml - UPI: ./UpiAccountExternalAccountInfo.yaml - NGN_ACCOUNT: ./NgnAccountExternalAccountInfo.yaml - CAD_ACCOUNT: ./CadAccountExternalAccountInfo.yaml - GBP_ACCOUNT: ./GbpAccountExternalAccountInfo.yaml - PHP_ACCOUNT: ./PhpAccountExternalAccountInfo.yaml - SGD_ACCOUNT: ./SgdAccountExternalAccountInfo.yaml - SPARK_WALLET: ./SparkWalletExternalAccountInfo.yaml - LIGHTNING: ./LightningExternalAccountInfo.yaml - SOLANA_WALLET: ./SolanaWalletExternalAccountInfo.yaml - TRON_WALLET: ./TronWalletExternalAccountInfo.yaml - POLYGON_WALLET: ./PolygonWalletExternalAccountInfo.yaml - BASE_WALLET: ./BaseWalletExternalAccountInfo.yaml diff --git a/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml deleted file mode 100644 index 32b939d4..00000000 --- a/openapi/components/schemas/external_accounts/BaseWalletExternalAccountInfo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/BaseWalletInfo.yaml - \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/BeneficiaryOneOf.yaml b/openapi/components/schemas/external_accounts/BeneficiaryOneOf.yaml deleted file mode 100644 index f7c7e1e5..00000000 --- a/openapi/components/schemas/external_accounts/BeneficiaryOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Individual Beneficiary - $ref: ./IndividualBeneficiary.yaml - - title: Business Beneficiary - $ref: ./BusinessBeneficiary.yaml -discriminator: - propertyName: beneficiaryType - mapping: - INDIVIDUAL: ./IndividualBeneficiary.yaml - BUSINESS: ./BusinessBeneficiary.yaml diff --git a/openapi/components/schemas/external_accounts/BeneficiaryType.yaml b/openapi/components/schemas/external_accounts/BeneficiaryType.yaml deleted file mode 100644 index b83d51a3..00000000 --- a/openapi/components/schemas/external_accounts/BeneficiaryType.yaml +++ /dev/null @@ -1,6 +0,0 @@ -type: string -enum: - - INDIVIDUAL - - BUSINESS -description: Whether the beneficiary is an individual or a business entity -example: INDIVIDUAL diff --git a/openapi/components/schemas/external_accounts/BusinessBeneficiary.yaml b/openapi/components/schemas/external_accounts/BusinessBeneficiary.yaml deleted file mode 100644 index bb6ae236..00000000 --- a/openapi/components/schemas/external_accounts/BusinessBeneficiary.yaml +++ /dev/null @@ -1,23 +0,0 @@ -allOf: - - $ref: ./BaseBeneficiary.yaml - - type: object - required: - - legalName - - beneficiaryType - properties: - beneficiaryType: - type: string - enum: - - BUSINESS - legalName: - type: string - description: Legal name of the business - example: Acme Corporation, Inc. - registrationNumber: - type: string - description: Business registration number - example: BRN-123456789 - taxId: - type: string - description: Tax identification number - example: EIN-987654321 diff --git a/openapi/components/schemas/external_accounts/CadAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/CadAccountExternalAccountInfo.yaml deleted file mode 100644 index 055b788c..00000000 --- a/openapi/components/schemas/external_accounts/CadAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/CadAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml diff --git a/openapi/components/schemas/external_accounts/ClabeAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/ClabeAccountExternalAccountInfo.yaml deleted file mode 100644 index e422f992..00000000 --- a/openapi/components/schemas/external_accounts/ClabeAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/ClabeAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/ExternalAccount.yaml b/openapi/components/schemas/external_accounts/ExternalAccount.yaml deleted file mode 100644 index 757d4f77..00000000 --- a/openapi/components/schemas/external_accounts/ExternalAccount.yaml +++ /dev/null @@ -1,38 +0,0 @@ -allOf: - - type: object - required: - - id - - status - - currency - - accountInfo - properties: - id: - type: string - description: The system generated identifier of this account - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - customerId: - type: string - description: The customer this account is tied to, or null if the account is on behalf of the platform. - example: Customer:da459a29-1fb7-41ce-a4cb-eb3a3c9fd7a7 - status: - $ref: ../external_accounts/ExternalAccountStatus.yaml - description: Status of the external account - example: ACTIVE - platformAccountId: - type: string - description: Optional platform-specific identifier for this account - example: acc_123456789 - currency: - type: string - description: The ISO 4217 currency code - example: USD - defaultUmaDepositAccount: - type: boolean - description: >- - Whether this account is the default UMA deposit account for the customer. If true, incoming UMA payments - to this customer's UMA address will be automatically deposited into this account instead of the primary internal account. - False if not provided. Note that at most, one external account can be set as the default UMA deposit account for a customer. - If there is no default UMA deposit account, incoming UMA payments will be deposited into the primary internal account for the customer. - example: false - accountInfo: - $ref: ./ExternalAccountInfoOneOf.yaml diff --git a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml b/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml deleted file mode 100644 index 4406cb73..00000000 --- a/openapi/components/schemas/external_accounts/ExternalAccountCreateRequest.yaml +++ /dev/null @@ -1,32 +0,0 @@ -allOf: - - type: object - required: - - currency - - accountInfo - properties: - customerId: - type: string - description: >- - The ID of the customer for whom to create the external account. - If not provided, the external account will be created on behalf of the platform. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: - type: string - description: The ISO 4217 currency code - example: USD - platformAccountId: - type: string - description: Your platform's identifier for the account in your system. This can be used to reference the account by your own identifier. - example: ext_acc_123456 - defaultUmaDepositAccount: - type: boolean - description: >- - Whether to set the external account as the default UMA deposit account. When set to true, - incoming payments to this customer's UMA address will be automatically deposited into this external account. - False if not provided. Note that only one external account can be set as - the default UMA deposit account for a customer, so if there is already a default UMA deposit account, - this will override the existing default UMA deposit account. If there is no default UMA deposit account, - incoming UMA payments will be deposited into the primary internal account for the customer. - default: false - accountInfo: - $ref: ./ExternalAccountInfoOneOf.yaml diff --git a/openapi/components/schemas/external_accounts/ExternalAccountInfoOneOf.yaml b/openapi/components/schemas/external_accounts/ExternalAccountInfoOneOf.yaml deleted file mode 100644 index 3833c6b3..00000000 --- a/openapi/components/schemas/external_accounts/ExternalAccountInfoOneOf.yaml +++ /dev/null @@ -1,52 +0,0 @@ -oneOf: - - title: US Account - $ref: ./UsAccountExternalAccountInfo.yaml - - title: CLABE Account - $ref: ./ClabeAccountExternalAccountInfo.yaml - - title: PIX Account - $ref: ./PixAccountExternalAccountInfo.yaml - - title: IBAN Account - $ref: ./IbanAccountExternalAccountInfo.yaml - - title: UPI Account - $ref: ./UpiAccountExternalAccountInfo.yaml - - title: NGN Account - $ref: ./NgnAccountExternalAccountInfo.yaml - - title: CAD Account - $ref: ./CadAccountExternalAccountInfo.yaml - - title: GBP Account - $ref: ./GbpAccountExternalAccountInfo.yaml - - title: PHP Account - $ref: ./PhpAccountExternalAccountInfo.yaml - - title: SGD Account - $ref: ./SgdAccountExternalAccountInfo.yaml - - title: Spark Wallet - $ref: ./SparkWalletExternalAccountInfo.yaml - - title: Lightning - $ref: ./LightningExternalAccountInfo.yaml - - title: Solana Wallet - $ref: ./SolanaWalletExternalAccountInfo.yaml - - title: Tron Wallet - $ref: ./TronWalletExternalAccountInfo.yaml - - title: Polygon Wallet - $ref: ./PolygonWalletExternalAccountInfo.yaml - - title: Base Wallet - $ref: ./BaseWalletExternalAccountInfo.yaml -discriminator: - propertyName: accountType - mapping: - US_ACCOUNT: ./UsAccountExternalAccountInfo.yaml - CLABE: ./ClabeAccountExternalAccountInfo.yaml - PIX: ./PixAccountExternalAccountInfo.yaml - IBAN: ./IbanAccountExternalAccountInfo.yaml - UPI: ./UpiAccountExternalAccountInfo.yaml - NGN_ACCOUNT: ./NgnAccountExternalAccountInfo.yaml - CAD_ACCOUNT: ./CadAccountExternalAccountInfo.yaml - GBP_ACCOUNT: ./GbpAccountExternalAccountInfo.yaml - PHP_ACCOUNT: ./PhpAccountExternalAccountInfo.yaml - SGD_ACCOUNT: ./SgdAccountExternalAccountInfo.yaml - SPARK_WALLET: ./SparkWalletExternalAccountInfo.yaml - LIGHTNING: ./LightningExternalAccountInfo.yaml - SOLANA_WALLET: ./SolanaWalletExternalAccountInfo.yaml - TRON_WALLET: ./TronWalletExternalAccountInfo.yaml - POLYGON_WALLET: ./PolygonWalletExternalAccountInfo.yaml - BASE_WALLET: ./BaseWalletExternalAccountInfo.yaml diff --git a/openapi/components/schemas/external_accounts/ExternalAccountStatus.yaml b/openapi/components/schemas/external_accounts/ExternalAccountStatus.yaml deleted file mode 100644 index bc349585..00000000 --- a/openapi/components/schemas/external_accounts/ExternalAccountStatus.yaml +++ /dev/null @@ -1,7 +0,0 @@ -type: string -enum: - - PENDING - - ACTIVE - - UNDER_REVIEW - - INACTIVE -description: Status of an external account diff --git a/openapi/components/schemas/external_accounts/ExternalAccountType.yaml b/openapi/components/schemas/external_accounts/ExternalAccountType.yaml deleted file mode 100644 index 66c8d8b3..00000000 --- a/openapi/components/schemas/external_accounts/ExternalAccountType.yaml +++ /dev/null @@ -1,20 +0,0 @@ -type: string -enum: - - US_ACCOUNT - - CLABE - - PIX - - IBAN - - UPI - - NGN_ACCOUNT - - CAD_ACCOUNT - - GBP_ACCOUNT - - PHP_ACCOUNT - - SGD_ACCOUNT - - SPARK_WALLET - - LIGHTNING - - SOLANA_WALLET - - TRON_WALLET - - POLYGON_WALLET - - BASE_WALLET -description: Type of external account or wallet -example: US_ACCOUNT diff --git a/openapi/components/schemas/external_accounts/GbpAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/GbpAccountExternalAccountInfo.yaml deleted file mode 100644 index d9d01c90..00000000 --- a/openapi/components/schemas/external_accounts/GbpAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/GbpAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml diff --git a/openapi/components/schemas/external_accounts/IbanAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/IbanAccountExternalAccountInfo.yaml deleted file mode 100644 index 085cdbc8..00000000 --- a/openapi/components/schemas/external_accounts/IbanAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/IbanAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/IndividualBeneficiary.yaml b/openapi/components/schemas/external_accounts/IndividualBeneficiary.yaml deleted file mode 100644 index 288ef94b..00000000 --- a/openapi/components/schemas/external_accounts/IndividualBeneficiary.yaml +++ /dev/null @@ -1,26 +0,0 @@ -allOf: - - $ref: ./BaseBeneficiary.yaml - - type: object - required: - - fullName - - birthDate - - nationality - - beneficiaryType - properties: - beneficiaryType: - type: string - enum: - - INDIVIDUAL - fullName: - type: string - description: Individual's full name - example: John Michael Doe - birthDate: - type: string - format: date - description: Date of birth in ISO 8601 format (YYYY-MM-DD) - example: '1990-01-15' - nationality: - type: string - description: Country code (ISO 3166-1 alpha-2) - example: US diff --git a/openapi/components/schemas/external_accounts/LightningExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/LightningExternalAccountInfo.yaml deleted file mode 100644 index 754c07c1..00000000 --- a/openapi/components/schemas/external_accounts/LightningExternalAccountInfo.yaml +++ /dev/null @@ -1,24 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - type: object - description: > - Lightning payment destination. Exactly one of `invoice`, `bolt12`, or `lightningAddress` must be provided. - required: - - accountType - properties: - accountType: - type: string - enum: - - LIGHTNING - invoice: - type: string - description: 1-time use lightning bolt11 invoice payout destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - bolt12: - type: string - description: A bolt12 offer which can be reused as a payment destination - example: lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs - lightningAddress: - type: string - description: A lightning address which can be used as a payment destination. Note that for UMA addresses, no external account is needed. You can use the UMA address directly as a destination. - example: john.doe@lightningwallet.com diff --git a/openapi/components/schemas/external_accounts/NgnAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/NgnAccountExternalAccountInfo.yaml deleted file mode 100644 index 959e1bfd..00000000 --- a/openapi/components/schemas/external_accounts/NgnAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,27 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/NgnAccountInfo.yaml - - type: object - required: - - purposeOfPayment - - beneficiary - properties: - purposeOfPayment: - type: string - enum: - - GIFT - - SELF - - GOODS_OR_SERVICES - - EDUCATION - - HEALTH_OR_MEDICAL - - REAL_ESTATE_PURCHASE - - LOAN_PAYMENT - - TAX_PAYMENT - - UTILITY_BILL - - DONATION - - TRAVEL - - OTHER - description: Purpose of payment - example: GOODS_OR_SERVICES - beneficiary: - $ref: ./BeneficiaryOneOf.yaml \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/PhpAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/PhpAccountExternalAccountInfo.yaml deleted file mode 100644 index d69becbe..00000000 --- a/openapi/components/schemas/external_accounts/PhpAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/PhpAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml diff --git a/openapi/components/schemas/external_accounts/PixAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/PixAccountExternalAccountInfo.yaml deleted file mode 100644 index f492c457..00000000 --- a/openapi/components/schemas/external_accounts/PixAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/PixAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml diff --git a/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml deleted file mode 100644 index d94a7581..00000000 --- a/openapi/components/schemas/external_accounts/PolygonWalletExternalAccountInfo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/PolygonWalletInfo.yaml - \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/SgdAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/SgdAccountExternalAccountInfo.yaml deleted file mode 100644 index 7e4cca1e..00000000 --- a/openapi/components/schemas/external_accounts/SgdAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/SgdAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml deleted file mode 100644 index 60fe22a8..00000000 --- a/openapi/components/schemas/external_accounts/SolanaWalletExternalAccountInfo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/SolanaWalletInfo.yaml - \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml deleted file mode 100644 index af530087..00000000 --- a/openapi/components/schemas/external_accounts/SparkWalletExternalAccountInfo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/SparkWalletInfo.yaml - \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml deleted file mode 100644 index 6ca2accc..00000000 --- a/openapi/components/schemas/external_accounts/TronWalletExternalAccountInfo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/TronWalletInfo.yaml - \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/UpiAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/UpiAccountExternalAccountInfo.yaml deleted file mode 100644 index e7675259..00000000 --- a/openapi/components/schemas/external_accounts/UpiAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/UpiAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml \ No newline at end of file diff --git a/openapi/components/schemas/external_accounts/UsAccountExternalAccountInfo.yaml b/openapi/components/schemas/external_accounts/UsAccountExternalAccountInfo.yaml deleted file mode 100644 index 6fa6a96e..00000000 --- a/openapi/components/schemas/external_accounts/UsAccountExternalAccountInfo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -allOf: - - $ref: ./BaseExternalAccountInfo.yaml - - $ref: ../common/UsAccountInfo.yaml - - type: object - required: - - beneficiary - properties: - beneficiary: - $ref: ./BeneficiaryOneOf.yaml \ No newline at end of file diff --git a/openapi/components/schemas/invitations/UmaInvitation.yaml b/openapi/components/schemas/invitations/UmaInvitation.yaml deleted file mode 100644 index 5861b99a..00000000 --- a/openapi/components/schemas/invitations/UmaInvitation.yaml +++ /dev/null @@ -1,68 +0,0 @@ -type: object -required: - - code - - createdAt - - inviterUma - - status - - url -properties: - code: - type: string - description: The unique code of the invitation - example: 019542f5 - createdAt: - type: string - format: date-time - description: When the invitation was created - example: '2025-09-01T14:30:00Z' - claimedAt: - type: string - format: date-time - description: When the invitation was claimed if it has been claimed - example: '2025-09-01T14:30:00Z' - url: - type: string - description: The URL where this invitation can be claimed. - example: https://uma.me/i/019542f5 - expiresAt: - type: string - format: date-time - description: When the invitation expires (if at all) - example: '2025-09-01T14:30:00Z' - inviterUma: - type: string - description: The UMA address of the inviter - example: $inviter@uma.domain - inviteeUma: - type: string - description: The UMA address of the invitee - example: $invitee@uma.domain - status: - type: string - enum: - - PENDING - - CLAIMED - - EXPIRED - - CANCELLED - description: The status of the invitation - example: PENDING - firstName: - type: string - description: The inviter's first name. Will be displayed when the recipient clicks the invite link - example: Jane - amountToSend: - $ref: ../common/CurrencyAmount.yaml - description: >- - The amount to send to the invitee when the invitation is claimed. This is - optional and if not provided, the invitee will not receive any amount. - Note that the actual sending of the amount must be done by the inviter - platform once the INVITATION_CLAIMED webhook is received. If the inviter - platform either does not send the payment or the payment fails, the - invitee will not receive this amount. This field is primarily used for - display purposes on the claiming side of the invitation. - - This field is useful for "send-by-link" style customer flows where an inviter - can send a payment simply by sharing a link without knowing the receiver's - UMA address. Note that these sends can only be sender-locked, meaning that - the sender will not know ahead of time how much the receiver will receive - in the receiving currency. diff --git a/openapi/components/schemas/plaid/PlaidCallbackRequest.yaml b/openapi/components/schemas/plaid/PlaidCallbackRequest.yaml deleted file mode 100644 index 1d84d100..00000000 --- a/openapi/components/schemas/plaid/PlaidCallbackRequest.yaml +++ /dev/null @@ -1,16 +0,0 @@ -type: object -required: - - publicToken -properties: - publicToken: - type: string - description: | - The public token returned by Plaid Link after the customer successfully - authenticates and selects an account. - example: public-sandbox-12345678-1234-1234-1234-123456789012 - accountId: - type: string - description: | - Optional Plaid account ID if the customer selected a specific account. - If not provided, the default account will be used. - example: plaid_account_id_123 diff --git a/openapi/components/schemas/plaid/PlaidCallbackResponse.yaml b/openapi/components/schemas/plaid/PlaidCallbackResponse.yaml deleted file mode 100644 index 3e3bfbab..00000000 --- a/openapi/components/schemas/plaid/PlaidCallbackResponse.yaml +++ /dev/null @@ -1,12 +0,0 @@ -type: object -required: - - message -properties: - message: - type: string - description: Human-readable message about the processing status - example: External account creation initiated. You will receive a webhook notification when complete. - requestId: - type: string - description: A unique identifier for this request, useful for debugging - example: req_abc123def456 diff --git a/openapi/components/schemas/plaid/PlaidLinkTokenRequest.yaml b/openapi/components/schemas/plaid/PlaidLinkTokenRequest.yaml deleted file mode 100644 index de1ae4b8..00000000 --- a/openapi/components/schemas/plaid/PlaidLinkTokenRequest.yaml +++ /dev/null @@ -1,8 +0,0 @@ -type: object -required: - - customerId -properties: - customerId: - type: string - description: The ID of the customer for whom to create the Plaid Link token and external account - example: Customer:019542f5-b3e7-1d02-0000-000000000001 diff --git a/openapi/components/schemas/plaid/PlaidLinkTokenResponse.yaml b/openapi/components/schemas/plaid/PlaidLinkTokenResponse.yaml deleted file mode 100644 index 68cf9e61..00000000 --- a/openapi/components/schemas/plaid/PlaidLinkTokenResponse.yaml +++ /dev/null @@ -1,30 +0,0 @@ -type: object -required: - - linkToken - - expiration - - callbackUrl -properties: - linkToken: - type: string - description: > - The Plaid Link token to be used to initialize Plaid Link in your application. - This token is single-use and expires after the specified expiration time. - example: link-sandbox-af1a0311-da53-4636-b754-dd15cc058176 - expiration: - type: string - format: date-time - description: > - The ISO 8601 timestamp when this link token expires. - Link tokens typically expire after 4 hours. - example: "2025-10-05T18:30:00Z" - callbackUrl: - type: string - description: > - The URL where the platform should POST the public_token after the customer - completes Plaid Link authentication. This will trigger asynchronous - external account creation. The URL includes the linkToken as the path parameter. - example: https://api.lightspark.com/grid/2025-10-13/plaid/callback/{plaid_link_token} - requestId: - type: string - description: A unique identifier for this request, useful for debugging - example: req_abc123def456 diff --git a/openapi/components/schemas/quotes/AccountDestination.yaml b/openapi/components/schemas/quotes/AccountDestination.yaml deleted file mode 100644 index 8b66265d..00000000 --- a/openapi/components/schemas/quotes/AccountDestination.yaml +++ /dev/null @@ -1,16 +0,0 @@ -allOf: - - $ref: ./BaseDestination.yaml - - type: object - required: - - accountId - - destinationType - properties: - destinationType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Destination account identifier - example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination account details diff --git a/openapi/components/schemas/quotes/AccountQuoteSource.yaml b/openapi/components/schemas/quotes/AccountQuoteSource.yaml deleted file mode 100644 index 6a31defc..00000000 --- a/openapi/components/schemas/quotes/AccountQuoteSource.yaml +++ /dev/null @@ -1,20 +0,0 @@ -allOf: - - $ref: ./BaseQuoteSource.yaml - - type: object - required: - - accountId - - sourceType - properties: - sourceType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Source account identifier - example: InternalAccount:85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - customerId: - type: string - description: Required when funding from an FBO account to identify the customer on whose behalf the transaction is being initiated. Otherwise, will default to the customerId of the account owner. - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - description: Source account details diff --git a/openapi/components/schemas/quotes/BaseDestination.yaml b/openapi/components/schemas/quotes/BaseDestination.yaml deleted file mode 100644 index c4c81646..00000000 --- a/openapi/components/schemas/quotes/BaseDestination.yaml +++ /dev/null @@ -1,12 +0,0 @@ -type: object -required: - - destinationType -properties: - destinationType: - $ref: ./DestinationType.yaml -discriminator: - propertyName: destinationType - mapping: - ACCOUNT: ./AccountDestination.yaml - UMA_ADDRESS: ./UmaAddressDestination.yaml - EXTERNAL_ACCOUNT_DETAILS: ./ExternalAccountDetailsDestination.yaml diff --git a/openapi/components/schemas/quotes/BaseQuoteSource.yaml b/openapi/components/schemas/quotes/BaseQuoteSource.yaml deleted file mode 100644 index f584b5ad..00000000 --- a/openapi/components/schemas/quotes/BaseQuoteSource.yaml +++ /dev/null @@ -1,11 +0,0 @@ -type: object -required: - - sourceType -properties: - sourceType: - $ref: ./QuoteSourceType.yaml -discriminator: - propertyName: sourceType - mapping: - ACCOUNT: ./AccountQuoteSource.yaml - REALTIME_FUNDING: ./RealtimeFundingQuoteSource.yaml diff --git a/openapi/components/schemas/quotes/DestinationType.yaml b/openapi/components/schemas/quotes/DestinationType.yaml deleted file mode 100644 index 9ebdbeba..00000000 --- a/openapi/components/schemas/quotes/DestinationType.yaml +++ /dev/null @@ -1,7 +0,0 @@ -type: string -enum: - - ACCOUNT - - UMA_ADDRESS - - EXTERNAL_ACCOUNT_DETAILS -description: Type of payment destination -example: ACCOUNT diff --git a/openapi/components/schemas/quotes/ExternalAccountDetailsDestination.yaml b/openapi/components/schemas/quotes/ExternalAccountDetailsDestination.yaml deleted file mode 100644 index 376083ff..00000000 --- a/openapi/components/schemas/quotes/ExternalAccountDetailsDestination.yaml +++ /dev/null @@ -1,18 +0,0 @@ -allOf: - - $ref: ./BaseDestination.yaml - - type: object - required: - - externalAccountDetails - - destinationType - properties: - destinationType: - type: string - enum: - - EXTERNAL_ACCOUNT_DETAILS - externalAccountDetails: - $ref: ../external_accounts/ExternalAccountCreateRequest.yaml - description: >- - A convenient destination option which adds the external account and creates - the quote in one step rather than first needing to call /external-accounts - to add the account. Useful for one-off payments to some destination. - See the external accounts endpoints for test values in sandbox mode. diff --git a/openapi/components/schemas/quotes/Quote.yaml b/openapi/components/schemas/quotes/Quote.yaml deleted file mode 100644 index 8802ab92..00000000 --- a/openapi/components/schemas/quotes/Quote.yaml +++ /dev/null @@ -1,104 +0,0 @@ -type: object -required: - - quoteId - - status - - expiresAt - - createdAt - - source - - destination - - sendingCurrency - - receivingCurrency - - totalSendingAmount - - totalReceivingAmount - - exchangeRate - - feesIncluded - - transactionId -properties: - quoteId: - type: string - description: Unique identifier for this quote - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - description: Current status of the quote - example: PENDING - createdAt: - type: string - format: date-time - description: When this quote was created - example: '2025-10-03T12:00:00Z' - expiresAt: - type: string - format: date-time - description: When this quote expires (typically 1-5 minutes after creation) - example: '2025-10-03T12:05:00Z' - # Transfer details - source: - $ref: ./QuoteSourceOneOf.yaml - destination: - $ref: ./QuoteDestinationOneOf.yaml - sendingCurrency: - $ref: ../common/Currency.yaml - description: Currency for the sending amount - receivingCurrency: - $ref: ../common/Currency.yaml - description: Currency for the receiving amount - totalSendingAmount: - type: integer - format: int64 - description: >- - The total amount that will be sent in the smallest unit of the sending - currency (eg. cents). - exclusiveMinimum: 0 - example: 123010 - totalReceivingAmount: - type: integer - format: int64 - description: >- - The total amount that will be received in the smallest unit of the - receiving currency (eg. cents). - exclusiveMinimum: 0 - example: 1000 - exchangeRate: - type: number - description: Number of sending currency units per receiving currency unit. - exclusiveMinimum: 0 - feesIncluded: - type: integer - format: int64 - description: >- - The fees associated with the quote in the smallest unit of the sending - currency (eg. cents). - minimum: 0 - example: 10 - paymentInstructions: - type: array - description: Payment instructions for executing the payment. This is not required when using an internal account source. - items: - $ref: ../common/PaymentInstructions.yaml - example: - - accountType: US_ACCOUNT - accountNumber: "1234567890" - routingNumber: "021000021" - bankName: "Chase Bank" - referenceCode: "REF123456" - - accountType: SPARK_WALLET - address: "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - invoice: "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" - transactionId: - type: string - description: The ID of the transaction created from this quote. - example: Transaction:019542f5-b3e7-1d02-0000-000000000005 - originalQuoteId: - description: ID of the quote that is being retried - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - rateDetails: - $ref: ../transactions/OutgoingRateDetails.yaml - description: Details about the rate and fees for the transaction. diff --git a/openapi/components/schemas/quotes/QuoteDestinationOneOf.yaml b/openapi/components/schemas/quotes/QuoteDestinationOneOf.yaml deleted file mode 100644 index 3bc5966c..00000000 --- a/openapi/components/schemas/quotes/QuoteDestinationOneOf.yaml +++ /dev/null @@ -1,13 +0,0 @@ -oneOf: - - title: Account - $ref: ./AccountDestination.yaml - - title: UMA Address - $ref: ./UmaAddressDestination.yaml - - title: External Account Details - $ref: ./ExternalAccountDetailsDestination.yaml -discriminator: - propertyName: destinationType - mapping: - ACCOUNT: ./AccountDestination.yaml - UMA_ADDRESS: ./UmaAddressDestination.yaml - EXTERNAL_ACCOUNT_DETAILS: ./ExternalAccountDetailsDestination.yaml diff --git a/openapi/components/schemas/quotes/QuoteLockSide.yaml b/openapi/components/schemas/quotes/QuoteLockSide.yaml deleted file mode 100644 index e31eb4e2..00000000 --- a/openapi/components/schemas/quotes/QuoteLockSide.yaml +++ /dev/null @@ -1,10 +0,0 @@ -type: string -enum: - - SENDING - - RECEIVING -description: >- - The side of the quote which should be locked and specified in the - `lockedCurrencyAmount`. For example, if I want to send exactly $5 MXN from my - wallet, I would set this to "sending", and the `lockedCurrencyAmount` to 500 - (in cents). If I want the receiver to receive exactly $10 USD, I would set - this to "receiving" and the `lockedCurrencyAmount` to 10000 (in cents). diff --git a/openapi/components/schemas/quotes/QuoteRequest.yaml b/openapi/components/schemas/quotes/QuoteRequest.yaml deleted file mode 100644 index aba685e0..00000000 --- a/openapi/components/schemas/quotes/QuoteRequest.yaml +++ /dev/null @@ -1,65 +0,0 @@ -type: object -required: - - source - - destination - - lockedCurrencySide - - lockedCurrencyAmount -properties: - lookupId: - type: string - description: >- - Lookup ID from a previous receiver lookup request. If provided, - this can make the quote creation more efficient by reusing cached lookup data. - - NOTE: This is required for UMA destinations due to counterparty institution requirements. - See `senderCustomerInfo` for more information. - example: Lookup:019542f5-b3e7-1d02-0000-000000000009 - source: - $ref: ./QuoteSourceOneOf.yaml - destination: - $ref: ./QuoteDestinationOneOf.yaml - lockedCurrencySide: - $ref: ./QuoteLockSide.yaml - lockedCurrencyAmount: - type: integer - format: int64 - description: >- - The amount to send/receive in the smallest unit of the locked - currency (eg. cents). See `lockedCurrencySide` for more - information. - exclusiveMinimum: 0 - maximum: 9000000000000000 - example: 1000 - immediatelyExecute: - type: boolean - description: >- - Whether to immediately execute the quote after creation. If true, - the quote will be executed and the transaction will be created at - the current exchange rate. It should only be used if you don't want - to lock and view rate details before executing the quote. If you are - executing a pre-existing quote, use the `/quotes/{quoteId}/execute` - endpoint instead. This is false by default. - example: false - description: - type: string - description: Optional description/memo for the transfer - example: 'Invoice #1234 payment' - senderCustomerInfo: - type: object - additionalProperties: true - description: > - Only relevant for UMA destinations. - - Key-value pairs of information about the sender which was - requested by the counterparty (recipient) institution. - - Any fields specified in `requiredPayerDataFields` from the - response of the `/receiver/uma/{receiverUmaAddress}` (lookupUma) - endpoint - - MUST be provided here if they were requested. If the - counterparty (recipient) institution did not request any - information, this field can be omitted. - example: - FULL_NAME: Jane Receiver - NATIONALITY: FR diff --git a/openapi/components/schemas/quotes/QuoteSourceOneOf.yaml b/openapi/components/schemas/quotes/QuoteSourceOneOf.yaml deleted file mode 100644 index 57a96df3..00000000 --- a/openapi/components/schemas/quotes/QuoteSourceOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Account - $ref: ./AccountQuoteSource.yaml - - title: Real-time Funding - $ref: ./RealtimeFundingQuoteSource.yaml -discriminator: - propertyName: sourceType - mapping: - ACCOUNT: ./AccountQuoteSource.yaml - REALTIME_FUNDING: ./RealtimeFundingQuoteSource.yaml diff --git a/openapi/components/schemas/quotes/QuoteSourceType.yaml b/openapi/components/schemas/quotes/QuoteSourceType.yaml deleted file mode 100644 index dd6f3a9e..00000000 --- a/openapi/components/schemas/quotes/QuoteSourceType.yaml +++ /dev/null @@ -1,6 +0,0 @@ -type: string -enum: - - ACCOUNT - - REALTIME_FUNDING -description: Type of quote funding source -example: ACCOUNT diff --git a/openapi/components/schemas/quotes/RealtimeFundingQuoteSource.yaml b/openapi/components/schemas/quotes/RealtimeFundingQuoteSource.yaml deleted file mode 100644 index a45b2940..00000000 --- a/openapi/components/schemas/quotes/RealtimeFundingQuoteSource.yaml +++ /dev/null @@ -1,29 +0,0 @@ -allOf: - - $ref: ./BaseQuoteSource.yaml - - type: object - required: - - currency - - sourceType - properties: - sourceType: - type: string - enum: - - REALTIME_FUNDING - customerId: - type: string - description: >- - Source customer ID. If this transaction is being initiated on behalf of a customer, this is required. - If customerId is not provided, the quote will be created on behalf of the platform itself. - example: Customer:019542f5-b3e7-1d02-0000-000000000009 - currency: - type: string - description: >- - Currency code for the funding source. See - [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) - for the full list of supported fiat and crypto currencies. - example: USD - description: >- - Fund the quote using a real-time funding source (RTP, SEPA Instant, Spark, Stables, etc.). - This will require manual just-in-time funding using `paymentInstructions` in the response. - Because quotes expire quickly, this option is only valid for instant payment methods. Do not try - to fund a quote with a non-instant payment method (ACH, etc.). diff --git a/openapi/components/schemas/quotes/UmaAddressDestination.yaml b/openapi/components/schemas/quotes/UmaAddressDestination.yaml deleted file mode 100644 index b60dbe34..00000000 --- a/openapi/components/schemas/quotes/UmaAddressDestination.yaml +++ /dev/null @@ -1,33 +0,0 @@ -allOf: - - $ref: ./BaseDestination.yaml - - type: object - required: - - umaAddress - - destinationType - properties: - destinationType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the recipient - example: $receiver@uma.domain.com - counterpartyInformation: - type: object - description: >- - Information about the recipient, as required by the platform in their - configuration. - additionalProperties: true - example: - FULL_NAME: Jane Receiver - BIRTH_DATE: '1990-01-01' - NATIONALITY: FR - currency: - type: string - description: >- - Currency code for the destination. See - [Supported Currencies](https://grid.lightspark.com/platform-overview/core-concepts/currencies-and-rails) - for the full list of supported fiat and crypto currencies. - example: EUR - description: UMA address destination details diff --git a/openapi/components/schemas/receiver/CurrencyPreference.yaml b/openapi/components/schemas/receiver/CurrencyPreference.yaml deleted file mode 100644 index cd808bf2..00000000 --- a/openapi/components/schemas/receiver/CurrencyPreference.yaml +++ /dev/null @@ -1,29 +0,0 @@ -type: object -required: - - currency - - estimatedExchangeRate - - min - - max -properties: - currency: - $ref: ../common/Currency.yaml - estimatedExchangeRate: - type: number - description: >- - An estimated exchange rate from the sender's currency to this currency. - This is not a locked rate and is subject to change when calling the quotes - endpoint. - exclusiveMinimum: 0 - example: 1.08 - min: - type: integer - format: int64 - description: The minimum amount that can be received in this currency. - exclusiveMinimum: 0 - example: 1 - max: - type: integer - format: int64 - description: The maximum amount that can be received in this currency. - exclusiveMinimum: 0 - example: 1000000 diff --git a/openapi/components/schemas/receiver/ReceiverLookupResponse.yaml b/openapi/components/schemas/receiver/ReceiverLookupResponse.yaml deleted file mode 100644 index 52ad43ec..00000000 --- a/openapi/components/schemas/receiver/ReceiverLookupResponse.yaml +++ /dev/null @@ -1,23 +0,0 @@ -type: object -required: - - supportedCurrencies - - lookupId -properties: - supportedCurrencies: - type: array - description: List of currencies supported by the receiving account - items: - $ref: ./CurrencyPreference.yaml - requiredPayerDataFields: - type: array - description: >- - Fields required by the receiving institution about the payer - before payment can be completed - items: - $ref: ../common/CounterpartyFieldDefinition.yaml - lookupId: - type: string - description: >- - Unique identifier for the lookup. Needed in the subsequent - create quote request. - example: Lookup:019542f5-b3e7-1d02-0000-000000000009 diff --git a/openapi/components/schemas/tokens/ApiToken.yaml b/openapi/components/schemas/tokens/ApiToken.yaml deleted file mode 100644 index 34d7d850..00000000 --- a/openapi/components/schemas/tokens/ApiToken.yaml +++ /dev/null @@ -1,45 +0,0 @@ -type: object -required: - - id - - name - - permissions - - clientId - - createdAt - - updatedAt -properties: - id: - type: string - description: System-generated unique identifier - example: Token:019542f5-b3e7-1d02-0000-000000000001 - name: - type: string - description: Name of the token - example: Sandbox read-only token - permissions: - type: array - description: A list of permissions granted to the token - items: - $ref: ./Permission.yaml - clientId: - type: string - description: >- - An opaque identifier that should be used as a client_id (or username) in - the HTTP Basic Authentication scheme when issuing http requests to Grid. - example: 01947d2284054f890000e63bca4810df - clientSecret: - type: string - description: >- - The secret that should be used to authenticate against Grid API. This - secret is not stored and will never be available again after creation. - Platform must keep this secret secure as it grants access to the account. - example: ed0ad25881e234cc28fb2dec0a4fe64e4172 - createdAt: - type: string - format: date-time - description: Creation timestamp - example: '2025-07-21T17:32:28Z' - updatedAt: - type: string - format: date-time - description: Last update timestamp - example: '2025-07-21T17:32:28Z' diff --git a/openapi/components/schemas/tokens/Permission.yaml b/openapi/components/schemas/tokens/Permission.yaml deleted file mode 100644 index 27dad139..00000000 --- a/openapi/components/schemas/tokens/Permission.yaml +++ /dev/null @@ -1,10 +0,0 @@ -type: string -enum: - - VIEW - - TRANSACT - - MANAGE -description: >- - Permission of an API token that determines what actions the token can perform: - VIEW: Can view all data, including platform config, customers and transactions - TRANSACT: Can send payments MANAGE: Can manage platform config, api tokens and - customers diff --git a/openapi/components/schemas/transactions/AccountTransactionDestination.yaml b/openapi/components/schemas/transactions/AccountTransactionDestination.yaml deleted file mode 100644 index cc041182..00000000 --- a/openapi/components/schemas/transactions/AccountTransactionDestination.yaml +++ /dev/null @@ -1,16 +0,0 @@ -allOf: - - $ref: ./BaseTransactionDestination.yaml - - type: object - required: - - accountId - - destinationType - properties: - destinationType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Destination account identifier - example: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination account details diff --git a/openapi/components/schemas/transactions/AccountTransactionSource.yaml b/openapi/components/schemas/transactions/AccountTransactionSource.yaml deleted file mode 100644 index a0862352..00000000 --- a/openapi/components/schemas/transactions/AccountTransactionSource.yaml +++ /dev/null @@ -1,16 +0,0 @@ -allOf: - - $ref: ./BaseTransactionSource.yaml - - type: object - required: - - accountId - - sourceType - properties: - sourceType: - type: string - enum: - - ACCOUNT - accountId: - type: string - description: Source account identifier - example: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Source account details diff --git a/openapi/components/schemas/transactions/BaseTransactionDestination.yaml b/openapi/components/schemas/transactions/BaseTransactionDestination.yaml deleted file mode 100644 index fab727b2..00000000 --- a/openapi/components/schemas/transactions/BaseTransactionDestination.yaml +++ /dev/null @@ -1,15 +0,0 @@ -type: object -required: - - destinationType -properties: - destinationType: - $ref: ./TransactionDestinationType.yaml - currency: - type: string - description: Currency code for the destination - example: EUR -discriminator: - propertyName: destinationType - mapping: - ACCOUNT: ./AccountTransactionDestination.yaml - UMA_ADDRESS: ./UmaAddressTransactionDestination.yaml diff --git a/openapi/components/schemas/transactions/BaseTransactionSource.yaml b/openapi/components/schemas/transactions/BaseTransactionSource.yaml deleted file mode 100644 index de82afa3..00000000 --- a/openapi/components/schemas/transactions/BaseTransactionSource.yaml +++ /dev/null @@ -1,15 +0,0 @@ -type: object -required: - - sourceType -properties: - sourceType: - $ref: ./TransactionSourceType.yaml - currency: - type: string - description: Currency code for the source - example: USD -discriminator: - propertyName: sourceType - mapping: - ACCOUNT: ./AccountTransactionSource.yaml - UMA_ADDRESS: ./UmaAddressTransactionSource.yaml diff --git a/openapi/components/schemas/transactions/IncomingRateDetails.yaml b/openapi/components/schemas/transactions/IncomingRateDetails.yaml deleted file mode 100644 index 26a95597..00000000 --- a/openapi/components/schemas/transactions/IncomingRateDetails.yaml +++ /dev/null @@ -1,41 +0,0 @@ -description: Details about the rate and fees for an incoming transaction. -type: object -required: - - gridApiMultiplier - - gridApiFixedFee - - gridApiVariableFeeRate - - gridApiVariableFeeAmount -properties: - gridApiMultiplier: - type: number - format: double - description: >- - The underlying multiplier from the mSATS to the receiving currency, - including variable fees. - exclusiveMinimum: 0 - example: 0.925 - gridApiFixedFee: - type: integer - format: int64 - description: >- - The fixed fee charged by the Grid product to execute the quote in the - smallest unit of the receiving currency (eg. cents). - minimum: 0 - example: 10 - gridApiVariableFeeRate: - type: number - format: double - description: >- - The variable fee rate charged by the Grid product to execute the quote - as a percentage of the receiving currency amount. - exclusiveMinimum: 0 - example: 0.003 - gridApiVariableFeeAmount: - type: number - format: int64 - description: >- - The variable fee amount charged by the Grid product to execute the quote - in the smallest unit of the receiving currency (eg. cents). This is the - receiving amount times gridApiVariableFeeRate. - minimum: 0 - example: 30 diff --git a/openapi/components/schemas/transactions/IncomingTransaction.yaml b/openapi/components/schemas/transactions/IncomingTransaction.yaml deleted file mode 100644 index 36e6db79..00000000 --- a/openapi/components/schemas/transactions/IncomingTransaction.yaml +++ /dev/null @@ -1,20 +0,0 @@ -allOf: - - $ref: ./Transaction.yaml - - type: object - required: - - receivedAmount - properties: - source: - $ref: ./TransactionSourceOneOf.yaml - receivedAmount: - $ref: ../common/CurrencyAmount.yaml - description: Amount received in the recipient's currency - reconciliationInstructions: - $ref: ../common/ReconciliationInstructions.yaml - description: Included for all transactions except those with "CREATED" status - rateDetails: - $ref: ./IncomingRateDetails.yaml - description: Details about the rate and fees for the transaction. - failureReason: - $ref: ./IncomingTransactionFailureReason.yaml - description: If the transaction failed, this field provides the reason for failure. diff --git a/openapi/components/schemas/transactions/IncomingTransactionFailureReason.yaml b/openapi/components/schemas/transactions/IncomingTransactionFailureReason.yaml deleted file mode 100644 index d4fe6373..00000000 --- a/openapi/components/schemas/transactions/IncomingTransactionFailureReason.yaml +++ /dev/null @@ -1,14 +0,0 @@ -type: string -enum: - - LNURLP_FAILED - - PAY_REQUEST_FAILED - - PAYMENT_APPROVAL_WEBHOOK_ERROR - - PAYMENT_APPROVAL_TIMED_OUT - - OFFRAMP_FAILED - - MISSING_MANDATORY_PAYEE_DATA - - QUOTE_EXPIRED - - QUOTE_EXECUTION_FAILED -description: >- - Reason for failure of an incoming transaction. This is used to provide more - context on why a transaction failed. If the transaction is not in a failed - state, this field is omitted. diff --git a/openapi/components/schemas/transactions/OutgoingRateDetails.yaml b/openapi/components/schemas/transactions/OutgoingRateDetails.yaml deleted file mode 100644 index e6beff62..00000000 --- a/openapi/components/schemas/transactions/OutgoingRateDetails.yaml +++ /dev/null @@ -1,59 +0,0 @@ -description: Details about the rate and fees for an outgoing transaction or quote. -type: object -required: - - counterpartyMultiplier - - counterpartyFixedFee - - gridApiMultiplier - - gridApiFixedFee - - gridApiVariableFeeRate - - gridApiVariableFeeAmount -properties: - counterpartyMultiplier: - type: number - format: double - description: >- - The underlying multiplier from mSATs to the receiving currency as returned - by the counterparty institution. - exclusiveMinimum: 0 - example: 1.08 - counterpartyFixedFee: - type: integer - format: int64 - description: >- - The fixed fee charged by the counterparty institution to execute the quote - in the smallest unit of the receiving currency (eg. cents). - minimum: 0 - example: 10 - gridApiMultiplier: - type: number - format: double - description: >- - The underlying multiplier from the sending currency to mSATS, including - variable fees. - exclusiveMinimum: 0 - example: 0.925 - gridApiFixedFee: - type: integer - format: int64 - description: >- - The fixed fee charged by the Grid product to execute the quote in the - smallest unit of the sending currency (eg. cents). - minimum: 0 - example: 10 - gridApiVariableFeeRate: - type: number - format: double - description: >- - The variable fee rate charged by the Grid product to execute the quote - as a percentage of the sending currency amount. - exclusiveMinimum: 0 - example: 0.003 - gridApiVariableFeeAmount: - type: number - format: int64 - description: >- - The variable fee amount charged by the Grid product to execute the quote - in the smallest unit of the sending currency (eg. cents). This is the - sending amount times gridApiVariableFeeRate. - minimum: 0 - example: 30 diff --git a/openapi/components/schemas/transactions/OutgoingTransaction.yaml b/openapi/components/schemas/transactions/OutgoingTransaction.yaml deleted file mode 100644 index 6eae9b86..00000000 --- a/openapi/components/schemas/transactions/OutgoingTransaction.yaml +++ /dev/null @@ -1,61 +0,0 @@ -allOf: - - $ref: ./Transaction.yaml - - type: object - required: - - sentAmount - - paymentInstructions - - source - properties: - source: - $ref: ./TransactionSourceOneOf.yaml - sentAmount: - $ref: ../common/CurrencyAmount.yaml - description: Amount sent in the sender's currency - receivedAmount: - $ref: ../common/CurrencyAmount.yaml - description: Amount to be received by recipient in the recipient's currency - exchangeRate: - type: number - description: Number of sending currency units per receiving currency unit. - exclusiveMinimum: 0 - example: 1.08 - fees: - type: integer - format: int64 - description: >- - The fees associated with the quote in the smallest unit of the sending - currency (eg. cents). - minimum: 0 - example: 10 - quoteId: - type: string - description: The ID of the quote that was used to trigger this payment - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - originalTransactionId: - type: string - description: >- - ID of the original transaction that this transaction is retrying, if applicable - example: Transaction:019542f5-b3e7-1d02-0000-000000000003 - paymentInstructions: - type: array - description: Payment instructions for executing the payment. - items: - $ref: ../common/PaymentInstructions.yaml - example: - - accountType: US_ACCOUNT - accountNumber: "1234567890" - routingNumber: "021000021" - bankName: "Chase Bank" - referenceCode: "REF123456" - - accountType: SPARK_WALLET - address: "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - invoice: "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs" - refund: - $ref: ../common/Refund.yaml - description: The refund if transaction was refunded. - rateDetails: - $ref: ./OutgoingRateDetails.yaml - description: Details about the rate and fees for the transaction. - failureReason: - $ref: ./OutgoingTransactionFailureReason.yaml - description: If the transaction failed, this field provides the reason for failure. diff --git a/openapi/components/schemas/transactions/OutgoingTransactionFailureReason.yaml b/openapi/components/schemas/transactions/OutgoingTransactionFailureReason.yaml deleted file mode 100644 index 4aa4fe88..00000000 --- a/openapi/components/schemas/transactions/OutgoingTransactionFailureReason.yaml +++ /dev/null @@ -1,12 +0,0 @@ -type: string -enum: - - QUOTE_EXPIRED - - QUOTE_EXECUTION_FAILED - - LIGHTNING_PAYMENT_FAILED - - FUNDING_AMOUNT_MISMATCH - - COUNTERPARTY_POST_TX_FAILED - - TIMEOUT -description: >- - Reason for failure of an outgoing transaction. This is used to provide more - context on why a transaction failed. If the transaction is not in a failed - state, this field is omitted. diff --git a/openapi/components/schemas/transactions/Transaction.yaml b/openapi/components/schemas/transactions/Transaction.yaml deleted file mode 100644 index fe0aa29b..00000000 --- a/openapi/components/schemas/transactions/Transaction.yaml +++ /dev/null @@ -1,61 +0,0 @@ -type: object -required: - - id - - status - - type - - destination - - customerId - - platformCustomerId -properties: - id: - type: string - description: Unique identifier for the transaction - example: Transaction:019542f5-b3e7-1d02-0000-000000000004 - status: - $ref: ./TransactionStatus.yaml - type: - $ref: ./TransactionType.yaml - destination: - $ref: ./TransactionDestinationOneOf.yaml - customerId: - type: string - description: System ID of the customer (sender for outgoing, recipient for incoming) - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: >- - Platform-specific ID of the customer (sender for outgoing, recipient for - incoming) - example: 18d3e5f7b4a9c2 - settledAt: - type: string - format: date-time - description: When the payment was or will be settled - example: '2025-08-15T14:30:00Z' - createdAt: - type: string - format: date-time - description: When the transaction was created - example: '2025-08-15T14:25:18Z' - updatedAt: - type: string - format: date-time - description: When the transaction was last updated - example: '2025-08-15T14:30:00Z' - description: - type: string - description: Optional memo or description for the payment - example: 'Payment for invoice #1234' - counterpartyInformation: - type: object - description: Additional information about the counterparty, if available and relevant to the transaction and platform. Only applicable for transactions to/from UMA addresses. - additionalProperties: true - example: - FULL_NAME: John Sender - BIRTH_DATE: '1985-06-15' - NATIONALITY: DE -discriminator: - propertyName: type - mapping: - INCOMING: ./IncomingTransaction.yaml - OUTGOING: ./OutgoingTransaction.yaml diff --git a/openapi/components/schemas/transactions/TransactionDestinationOneOf.yaml b/openapi/components/schemas/transactions/TransactionDestinationOneOf.yaml deleted file mode 100644 index eca20ef5..00000000 --- a/openapi/components/schemas/transactions/TransactionDestinationOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Account Destination - $ref: ./AccountTransactionDestination.yaml - - title: UMA Address Destination - $ref: ./UmaAddressTransactionDestination.yaml -discriminator: - propertyName: destinationType - mapping: - ACCOUNT: ./AccountTransactionDestination.yaml - UMA_ADDRESS: ./UmaAddressTransactionDestination.yaml diff --git a/openapi/components/schemas/transactions/TransactionDestinationType.yaml b/openapi/components/schemas/transactions/TransactionDestinationType.yaml deleted file mode 100644 index f2b90067..00000000 --- a/openapi/components/schemas/transactions/TransactionDestinationType.yaml +++ /dev/null @@ -1,6 +0,0 @@ -type: string -enum: - - ACCOUNT - - UMA_ADDRESS -description: Type of transaction destination -example: ACCOUNT diff --git a/openapi/components/schemas/transactions/TransactionSourceOneOf.yaml b/openapi/components/schemas/transactions/TransactionSourceOneOf.yaml deleted file mode 100644 index 06c8e03d..00000000 --- a/openapi/components/schemas/transactions/TransactionSourceOneOf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -oneOf: - - title: Account Source - $ref: ./AccountTransactionSource.yaml - - title: UMA Address Source - $ref: ./UmaAddressTransactionSource.yaml -discriminator: - propertyName: sourceType - mapping: - ACCOUNT: ./AccountTransactionSource.yaml - UMA_ADDRESS: ./UmaAddressTransactionSource.yaml diff --git a/openapi/components/schemas/transactions/TransactionSourceType.yaml b/openapi/components/schemas/transactions/TransactionSourceType.yaml deleted file mode 100644 index 469546cf..00000000 --- a/openapi/components/schemas/transactions/TransactionSourceType.yaml +++ /dev/null @@ -1,7 +0,0 @@ -type: string -enum: - - ACCOUNT - - UMA_ADDRESS - - REALTIME_FUNDING -description: Type of transaction source -example: ACCOUNT diff --git a/openapi/components/schemas/transactions/TransactionStatus.yaml b/openapi/components/schemas/transactions/TransactionStatus.yaml deleted file mode 100644 index 67619ca8..00000000 --- a/openapi/components/schemas/transactions/TransactionStatus.yaml +++ /dev/null @@ -1,11 +0,0 @@ -type: string -enum: - - CREATED - - PENDING - - PROCESSING - - COMPLETED - - REJECTED - - FAILED - - REFUNDED - - EXPIRED -description: Status of a payment transaction diff --git a/openapi/components/schemas/transactions/TransactionType.yaml b/openapi/components/schemas/transactions/TransactionType.yaml deleted file mode 100644 index 2ddc7e1e..00000000 --- a/openapi/components/schemas/transactions/TransactionType.yaml +++ /dev/null @@ -1,5 +0,0 @@ -type: string -enum: - - INCOMING - - OUTGOING -description: Type of transaction (incoming payment or outgoing payment) diff --git a/openapi/components/schemas/transactions/UmaAddressTransactionDestination.yaml b/openapi/components/schemas/transactions/UmaAddressTransactionDestination.yaml deleted file mode 100644 index 8d1b5dc0..00000000 --- a/openapi/components/schemas/transactions/UmaAddressTransactionDestination.yaml +++ /dev/null @@ -1,16 +0,0 @@ -allOf: - - $ref: ./BaseTransactionDestination.yaml - - type: object - required: - - umaAddress - - destinationType - properties: - destinationType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the recipient - example: $receiver@uma.domain.com - description: UMA address destination details diff --git a/openapi/components/schemas/transactions/UmaAddressTransactionSource.yaml b/openapi/components/schemas/transactions/UmaAddressTransactionSource.yaml deleted file mode 100644 index 6bf36781..00000000 --- a/openapi/components/schemas/transactions/UmaAddressTransactionSource.yaml +++ /dev/null @@ -1,16 +0,0 @@ -allOf: - - $ref: ./BaseTransactionSource.yaml - - type: object - required: - - umaAddress - - sourceType - properties: - sourceType: - type: string - enum: - - UMA_ADDRESS - umaAddress: - type: string - description: UMA address of the sender - example: $sender@uma.domain.com - description: UMA address source details diff --git a/openapi/components/schemas/uma_providers/UmaProvider.yaml b/openapi/components/schemas/uma_providers/UmaProvider.yaml deleted file mode 100644 index ff75d58c..00000000 --- a/openapi/components/schemas/uma_providers/UmaProvider.yaml +++ /dev/null @@ -1,39 +0,0 @@ -type: object -properties: - name: - type: string - description: Name of the UMA Provider - example: "Lightspark Group" - supportedRegions: - type: array - items: - type: string - description: Region(s) this UMA Provider operates in - example: ["US"] - domain: - type: string - description: Domain this VASP uses for UMA addresses - example: "uma.me" - logoUrl: - type: string - description: Logo URL for the VASP - format: uri - example: "https://uma.me/logo.png" - supportedCurrencies: - type: array - items: - $ref: '../common/Currency.yaml' - description: List of currencies supported by this UMA Provider - example: - - code: "USD" - name: "United States Dollar" - symbol: "$" - decimals: 2 - lei: - type: string - description: Legal Entity Identifier for the UMA Provider - example: "5493001KJTIIGC8Y1R12" - allowListStatus: - type: boolean - description: Whether this UMA Provider is on your allow list - example: true \ No newline at end of file diff --git a/openapi/components/schemas/webhooks/AccountStatusWebhook.yaml b/openapi/components/schemas/webhooks/AccountStatusWebhook.yaml deleted file mode 100644 index 522e83ce..00000000 --- a/openapi/components/schemas/webhooks/AccountStatusWebhook.yaml +++ /dev/null @@ -1,21 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - required: - - accountId - properties: - accountId: - type: string - description: The id of the account whose balance has changed - oldBalance: - $ref: ../common/CurrencyAmount.yaml - newBalance: - $ref: ../common/CurrencyAmount.yaml - customerId: - type: string - description: The ID of the customer associated with the internal account - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: - type: string - description: The ID of the customer as associated in your platform - example: 019542f5-b3e7-1d02-0000-000000000001 diff --git a/openapi/components/schemas/webhooks/BaseWebhook.yaml b/openapi/components/schemas/webhooks/BaseWebhook.yaml deleted file mode 100644 index b2344204..00000000 --- a/openapi/components/schemas/webhooks/BaseWebhook.yaml +++ /dev/null @@ -1,31 +0,0 @@ -type: object -required: - - timestamp - - webhookId - - type -properties: - timestamp: - type: string - format: date-time - description: >- - ISO8601 timestamp when the webhook was sent (can be used to - prevent replay attacks) - example: '2025-08-15T14:32:00Z' - webhookId: - type: string - description: >- - Unique identifier for this webhook delivery (can be used for - idempotency) - example: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: - $ref: ./WebhookType.yaml - description: Type of webhook event -discriminator: - propertyName: type - mapping: - INCOMING_PAYMENT: ./IncomingPaymentWebhook.yaml - OUTGOING_PAYMENT: ./OutgoingPaymentWebhook.yaml - TEST: ./TestWebhookRequest.yaml - BULK_UPLOAD: ../customers/BulkUploadWebhookRequest.yaml - INVITATION_CLAIMED: ./InvitationClaimedWebhook.yaml - KYC_STATUS: ./KycStatusWebhook.yaml diff --git a/openapi/components/schemas/webhooks/IncomingPaymentWebhook.yaml b/openapi/components/schemas/webhooks/IncomingPaymentWebhook.yaml deleted file mode 100644 index df21f150..00000000 --- a/openapi/components/schemas/webhooks/IncomingPaymentWebhook.yaml +++ /dev/null @@ -1,21 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - required: - - transaction - properties: - transaction: - $ref: ../transactions/IncomingTransaction.yaml - type: - $ref: ./WebhookType.yaml - description: Type of webhook event - example: INCOMING_PAYMENT - requestedReceiverCustomerInfoFields: - type: array - items: - $ref: ../common/CounterpartyFieldDefinition.yaml - description: >- - Information required by the sender's VASP about the recipient. - Platform must provide these in the 200 OK response if approving. - Note that this only includes fields which Grid does not - already have from initial customer registration. diff --git a/openapi/components/schemas/webhooks/IncomingPaymentWebhookForbiddenResponse.yaml b/openapi/components/schemas/webhooks/IncomingPaymentWebhookForbiddenResponse.yaml deleted file mode 100644 index 4d8b6c0d..00000000 --- a/openapi/components/schemas/webhooks/IncomingPaymentWebhookForbiddenResponse.yaml +++ /dev/null @@ -1,11 +0,0 @@ -allOf: - - $ref: ../common/GridError.yaml - - type: object - properties: - reason: - type: string - description: >- - Optional reason for rejecting the payment. This is just - for debugging purposes or can be used for a platform's own - purposes. - example: RESTRICTED_JURISDICTION \ No newline at end of file diff --git a/openapi/components/schemas/webhooks/IncomingPaymentWebhookResponse.yaml b/openapi/components/schemas/webhooks/IncomingPaymentWebhookResponse.yaml deleted file mode 100644 index d0e0c500..00000000 --- a/openapi/components/schemas/webhooks/IncomingPaymentWebhookResponse.yaml +++ /dev/null @@ -1,9 +0,0 @@ -type: object -properties: - receiverCustomerInfo: - type: object - additionalProperties: true - description: >- - Information about the recipient, provided by the platform if - requested in the webhook via `requestedReceiverCustomerInfoFields` - and the payment is approved. diff --git a/openapi/components/schemas/webhooks/IncomingPaymentWebhookUnprocessableResponse.yaml b/openapi/components/schemas/webhooks/IncomingPaymentWebhookUnprocessableResponse.yaml deleted file mode 100644 index 543ecce4..00000000 --- a/openapi/components/schemas/webhooks/IncomingPaymentWebhookUnprocessableResponse.yaml +++ /dev/null @@ -1,14 +0,0 @@ -allOf: - - $ref: ../common/GridError.yaml - - type: object - properties: - requiredFields: - type: array - items: - type: string - description: >- - List of fields that are required by the platform, but are - not present in the counterparty information. - example: - - TAX_ID - - REGISTRATION_NUMBER \ No newline at end of file diff --git a/openapi/components/schemas/webhooks/InvitationClaimedWebhook.yaml b/openapi/components/schemas/webhooks/InvitationClaimedWebhook.yaml deleted file mode 100644 index a9087f37..00000000 --- a/openapi/components/schemas/webhooks/InvitationClaimedWebhook.yaml +++ /dev/null @@ -1,13 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - required: - - invitation - properties: - invitation: - $ref: ../invitations/UmaInvitation.yaml - type: - type: string - enum: [INVITATION_CLAIMED] - description: Type of webhook event - example: INVITATION_CLAIMED diff --git a/openapi/components/schemas/webhooks/KycStatusWebhook.yaml b/openapi/components/schemas/webhooks/KycStatusWebhook.yaml deleted file mode 100644 index 082e71c4..00000000 --- a/openapi/components/schemas/webhooks/KycStatusWebhook.yaml +++ /dev/null @@ -1,13 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - required: - - customerId - - kycStatus - properties: - customerId: - type: string - description: System generated id of the customer - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - kycStatus: - $ref: ../customers/KycStatus.yaml diff --git a/openapi/components/schemas/webhooks/OutgoingPaymentWebhook.yaml b/openapi/components/schemas/webhooks/OutgoingPaymentWebhook.yaml deleted file mode 100644 index 948a828d..00000000 --- a/openapi/components/schemas/webhooks/OutgoingPaymentWebhook.yaml +++ /dev/null @@ -1,12 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - required: - - transaction - properties: - transaction: - $ref: ../transactions/OutgoingTransaction.yaml - type: - $ref: ./WebhookType.yaml - description: Type of webhook event - example: OUTGOING_PAYMENT diff --git a/openapi/components/schemas/webhooks/TestWebhookRequest.yaml b/openapi/components/schemas/webhooks/TestWebhookRequest.yaml deleted file mode 100644 index c7282438..00000000 --- a/openapi/components/schemas/webhooks/TestWebhookRequest.yaml +++ /dev/null @@ -1,8 +0,0 @@ -allOf: - - $ref: ./BaseWebhook.yaml - - type: object - properties: - type: - $ref: ./WebhookType.yaml - description: Type of webhook event - example: TEST diff --git a/openapi/components/schemas/webhooks/TestWebhookResponse.yaml b/openapi/components/schemas/webhooks/TestWebhookResponse.yaml deleted file mode 100644 index 875993ef..00000000 --- a/openapi/components/schemas/webhooks/TestWebhookResponse.yaml +++ /dev/null @@ -1,18 +0,0 @@ -type: object -required: - - response_status -properties: - url: - type: string - format: uri - description: URL where the webhook was sent - example: https://api.mycompany.com/webhooks/uma - response_status: - type: integer - description: The HTTP status code returned by the webhook endpoint - example: 200 - response_body: - type: string - description: >- - The raw body content returned by the webhook endpoint in response to the - request diff --git a/openapi/components/schemas/webhooks/WebhookType.yaml b/openapi/components/schemas/webhooks/WebhookType.yaml deleted file mode 100644 index 6ed69c8c..00000000 --- a/openapi/components/schemas/webhooks/WebhookType.yaml +++ /dev/null @@ -1,12 +0,0 @@ -type: string -enum: - - INCOMING_PAYMENT - - OUTGOING_PAYMENT - - TEST - - BULK_UPLOAD - - INVITATION_CLAIMED - - KYC_STATUS - - ACCOUNT_STATUS -description: >- - Type of webhook event, used by the receiver to identify which webhook is being - received diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml deleted file mode 100644 index 822bbce7..00000000 --- a/openapi/openapi.yaml +++ /dev/null @@ -1,169 +0,0 @@ -openapi: 3.1.0 -info: - title: Grid API - description: > - API for managing global payments on the open Money Grid. Built by Lightspark. See the full documentation at https://grid.lightspark.com/. - version: "2025-10-13" - contact: - name: Lightspark Support - email: support@lightspark.com - license: - name: Proprietary - url: https://lightspark.com/terms -tags: - - name: Platform Configuration - description: >- - Platform configuration endpoints for managing global settings. You can - also configure these settings in the Grid dashboard. - - name: Customers - description: Customer management endpoints for creating and updating customer information - - name: Internal Accounts - description: Internal account management endpoints for creating and managing internal accounts - - name: External Accounts - description: External account management endpoints for creating and managing external bank accounts - - name: Same-Currency Transfers - description: Endpoints for transferring funds between internal and external accounts with the same currency - - name: Cross-Currency Transfers - description: Endpoints for creating and confirming quotes for cross-currency transfers - - name: Transactions - description: Endpoints for retrieving transaction information - - name: Webhooks - description: Webhook endpoints and configuration for receiving notifications - - name: Invitations - description: Endpoints for creating, claiming and managing UMA invitations - - name: Sandbox - description: Endpoints to trigger test cases in sandbox - - name: API Tokens - description: Endpoints to programmatically manage API tokens -servers: - - url: https://api.lightspark.com/grid/2025-10-13 - description: Production server -components: - securitySchemes: - BasicAuth: - type: http - scheme: basic - description: >- - API token authentication using format `:` - WebhookSignature: - type: apiKey - in: header - name: X-Grid-Signature - description: > - Secp256r1 (P-256) asymmetric signature of the webhook payload, which can - be used to verify that the webhook was sent by Grid. - - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If - not, it should be rejected. - schemas: - AllErrors: - anyOf: - - $ref: components/schemas/errors/Error400.yaml - - $ref: components/schemas/errors/Error401.yaml - - $ref: components/schemas/errors/Error403.yaml - - $ref: components/schemas/errors/Error404.yaml - - $ref: components/schemas/errors/Error409.yaml - - $ref: components/schemas/errors/Error410.yaml - - $ref: components/schemas/errors/Error412.yaml - - $ref: components/schemas/errors/Error424.yaml - - $ref: components/schemas/errors/Error500.yaml - - $ref: components/schemas/errors/Error501.yaml -paths: - /config: - $ref: paths/platform/config.yaml - /customers: - $ref: paths/customers/customers.yaml - /customers/{customerId}: - $ref: paths/customers/customers_{customerId}.yaml - /customers/kyc-link: - $ref: paths/customers/customers_kyc_link.yaml - /customers/internal-accounts: - $ref: paths/customers/customers_internal_accounts.yaml - /platform/internal-accounts: - $ref: paths/platform/platform_internal_accounts.yaml - /customers/external-accounts: - $ref: paths/customers/customers_external_accounts.yaml - /platform/external-accounts: - $ref: paths/platform/platform_external_accounts.yaml - /plaid/link-tokens: - $ref: paths/plaid/plaid_link-tokens.yaml - /plaid/callback/{plaid_link_token}: - $ref: paths/plaid/plaid_callback_{plaid_link_token}.yaml - /transfer-in: - $ref: paths/transfers/transfer_in.yaml - /transfer-out: - $ref: paths/transfers/transfer_out.yaml - /receiver/uma/{receiverUmaAddress}: - $ref: paths/receiver/uma/receiver_uma_{receiverUmaAddress}.yaml - /receiver/external-account/{accountId}: - $ref: paths/receiver/external-account/receiver_external-account_{accountId}.yaml - /quotes/{quoteId}: - $ref: paths/quotes/quotes_{quoteId}.yaml - /quotes: - $ref: paths/quotes/quotes.yaml - /quotes/{quoteId}/execute: - $ref: paths/quotes/quotes_{quoteId}_execute.yaml - /transactions: - $ref: paths/transactions/transactions.yaml - /transactions/{transactionId}: - $ref: paths/transactions/transactions_{transactionId}.yaml - /transactions/{transactionId}/approve: - $ref: paths/transactions/transactions_{transactionId}_approve.yaml - /transactions/{transactionId}/reject: - $ref: paths/transactions/transactions_{transactionId}_reject.yaml - /webhooks/test: - $ref: paths/webhooks/webhooks_test.yaml - /customers/bulk/csv: - $ref: paths/customers/customers_bulk_csv.yaml - /customers/bulk/jobs/{jobId}: - $ref: paths/customers/customers_bulk_jobs_{jobId}.yaml - /invitations: - $ref: paths/invitations/invitations.yaml - /invitations/{invitationCode}: - $ref: paths/invitations/invitations_{invitationCode}.yaml - /invitations/{invitationCode}/claim: - $ref: paths/invitations/invitations_{invitationCode}_claim.yaml - /invitations/{invitationCode}/cancel: - $ref: paths/invitations/invitations_{invitationCode}_cancel.yaml - /sandbox/send: - $ref: paths/sandbox/sandbox_send.yaml - /sandbox/uma/receive: - $ref: paths/sandbox/sandbox_uma_receive.yaml - /sandbox/internal-accounts/{accountId}/fund: - $ref: paths/sandbox/sandbox_internal_accounts_{accountId}_fund.yaml - /uma-providers: - $ref: paths/uma_providers/uma_providers.yaml - /tokens: - $ref: paths/tokens/tokens.yaml - /tokens/{tokenId}: - $ref: paths/tokens/tokens_{tokenId}.yaml -webhooks: - incoming-payment: - $ref: webhooks/incoming-payment.yaml - outgoing-payment: - $ref: webhooks/outgoing-payment.yaml - test-webhook: - $ref: webhooks/test-webhook.yaml - bulk-upload: - $ref: webhooks/bulk-upload.yaml - invitation-claimed: - $ref: webhooks/invitation-claimed.yaml - kyc-status: - $ref: webhooks/kyc-status.yaml - account-status: - $ref: webhooks/account-status.yaml -security: - - BasicAuth: [] diff --git a/openapi/paths/account/account.yaml b/openapi/paths/account/account.yaml deleted file mode 100644 index dda1df08..00000000 --- a/openapi/paths/account/account.yaml +++ /dev/null @@ -1,86 +0,0 @@ -get: - summary: List internal accounts - description: > - Retrieve a list of internal accounts with optional filtering parameters. Returns all - internal accounts that match the specified filters. If no filters are provided, returns all internal accounts - (paginated). - operationId: listInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by internal accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: ../../components/schemas/customers/InternalAccount.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of customers matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/customers/customers.yaml b/openapi/paths/customers/customers.yaml deleted file mode 100644 index 719b3d21..00000000 --- a/openapi/paths/customers/customers.yaml +++ /dev/null @@ -1,181 +0,0 @@ -post: - summary: Add a new customer - description: >- - Register a new customer in the system with an account identifier and bank account - information - operationId: createCustomer - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/customers/CustomerCreateRequestOneOf.yaml - responses: - '201': - description: Customer created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/customers/CustomerOneOf.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Customer with the UMA address already exists - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - '501': - description: Not implemented - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error501.yaml -get: - summary: List customers - description: > - Retrieve a list of customers with optional filtering parameters. Returns all - customers that match - - the specified filters. If no filters are provided, returns all customers - (paginated). - operationId: listCustomers - tags: - - Customers - security: - - BasicAuth: [] - parameters: - - name: platformCustomerId - in: query - description: Filter by platform-specific customer identifier - required: false - schema: - type: string - - name: customerType - in: query - description: Filter by customer type - required: false - schema: - $ref: ../../components/schemas/customers/CustomerType.yaml - - name: createdAfter - in: query - description: Filter customers created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter customers created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedAfter - in: query - description: Filter customers updated after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedBefore - in: query - description: Filter customers updated before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: umaAddress - in: query - description: Filter by uma address - required: false - schema: - type: string - - name: isIncludingDeleted - in: query - description: Whether to include deleted customers in the results. Default is false. - required: false - schema: - type: boolean - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of customers matching the filter criteria - items: - $ref: ../../components/schemas/customers/CustomerOneOf.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of customers matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/customers/customers_bulk_csv.yaml b/openapi/paths/customers/customers_bulk_csv.yaml deleted file mode 100644 index bfcc6297..00000000 --- a/openapi/paths/customers/customers_bulk_csv.yaml +++ /dev/null @@ -1,146 +0,0 @@ -post: - summary: Upload customers via CSV file - description: > - Upload a CSV file containing customer information for bulk creation. The CSV - file should follow - - a specific format with required and optional columns based on customer type. - - - ### CSV Format - - The CSV file should have the following columns: - - - Required columns for all customers: - - - umaAddress: The customer's UMA address (e.g., $john.doe@uma.domain.com) - - - platformCustomerId: Your platform's unique identifier for the customer - - - customerType: Either "INDIVIDUAL" or "BUSINESS" - - - Required columns for individual customers: - - - fullName: Individual's full name - - - birthDate: Date of birth in YYYY-MM-DD format - - - addressLine1: Street address line 1 - - - city: City - - - state: State/Province/Region - - - postalCode: Postal/ZIP code - - - country: Country code (ISO 3166-1 alpha-2) - - - Required columns for business customers: - - - businessLegalName: Legal name of the business - - - addressLine1: Street address line 1 - - - city: City - - - state: State/Province/Region - - - postalCode: Postal/ZIP code - - - country: Country code (ISO 3166-1 alpha-2) - - - Optional columns for all customers: - - - addressLine2: Street address line 2 - - - platformAccountId: Your platform's identifier for the bank account - - - description: Optional description for the customer - - - Optional columns for individual customers: - - - email: Customer's email address - - - Optional columns for business customers: - - - businessRegistrationNumber: Business registration number - - - businessTaxId: Tax identification number - - - ### Example CSV - - ```csv - - umaAddress,platformCustomerId,customerType,fullName,birthDate,addressLine1,city,state,postalCode,country,platformAccountId,businessLegalName - - john.doe@uma.domain.com,customer123,INDIVIDUAL,John Doe,1990-01-15,123 Main - St,San Francisco,CA,94105,US - - acme@uma.domain.com,biz456,BUSINESS,,,400 Commerce - Way,Austin,TX,78701,US - - ``` - - - The upload process is asynchronous and will return a job ID that can be used - to track progress. - - You can monitor the job status using the `/customers/bulk/jobs/{jobId}` - endpoint. - operationId: uploadCustomersCsv - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - file - properties: - file: - type: string - format: binary - description: CSV file containing customer information - responses: - '202': - description: CSV upload accepted for processing - content: - application/json: - schema: - type: object - required: - - jobId - - status - properties: - jobId: - type: string - description: Unique identifier for the bulk import job - example: Job:019542f5-b3e7-1d02-0000-000000000006 - status: - type: string - enum: - - PENDING - - PROCESSING - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/customers/customers_bulk_jobs_{jobId}.yaml b/openapi/paths/customers/customers_bulk_jobs_{jobId}.yaml deleted file mode 100644 index 709270e0..00000000 --- a/openapi/paths/customers/customers_bulk_jobs_{jobId}.yaml +++ /dev/null @@ -1,55 +0,0 @@ -get: - summary: Get bulk import job status - description: > - Retrieve the current status and results of a bulk customer import job. This - endpoint can be used - - to track the progress of both CSV uploads. - - - The response includes: - - - Overall job status - - - Progress statistics - - - Detailed error information for failed entries - - - Completion timestamp when finished - operationId: getBulkCustomerImportJob - tags: - - Customers - security: - - BasicAuth: [] - parameters: - - name: jobId - in: path - description: ID of the bulk import job to retrieve - required: true - schema: - type: string - responses: - '200': - description: Job status retrieved successfully - content: - application/json: - schema: - $ref: ../../components/schemas/customers/BulkCustomerImportJob.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Job not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/customers/customers_external_accounts.yaml b/openapi/paths/customers/customers_external_accounts.yaml deleted file mode 100644 index b9bb0ffd..00000000 --- a/openapi/paths/customers/customers_external_accounts.yaml +++ /dev/null @@ -1,182 +0,0 @@ -get: - summary: List Customer external accounts - description: | - Retrieve a list of external accounts with optional filtering parameters. Returns all - external accounts that match the specified filters. If no filters are provided, returns all external accounts - (paginated). - - External accounts are bank accounts, cryptocurrency wallets, or other payment destinations that customers can use to receive funds from the platform. - operationId: listCustomerExternalAccounts - tags: - - External Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by external accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: ../../components/schemas/external_accounts/ExternalAccount.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of external accounts matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - -post: - summary: Add a new external account - description: >- - Register a new external bank account for a customer. - - - **Sandbox Testing:** In sandbox mode, use these account number patterns to test different transfer scenarios. - These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. - For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - - Any other account number: Success (transfers complete normally) - operationId: createCustomerExternalAccount - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/external_accounts/ExternalAccountCreateRequest.yaml - examples: - usBankAccount: - summary: Create external US bank account - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: USD - accountInfo: - accountType: US_ACCOUNT - accountNumber: "12345678901" - routingNumber: "123456789" - accountCategory: CHECKING - bankName: Chase Bank - platformAccountId: ext_acc_123456 - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: "1990-01-15" - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: "94105" - country: US - sparkWallet: - summary: Create external Spark wallet - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - responses: - '201': - description: External account created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/external_accounts/ExternalAccount.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '409': - description: Conflict - External account already exists - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/customers/customers_internal_accounts.yaml b/openapi/paths/customers/customers_internal_accounts.yaml deleted file mode 100644 index ef6f77ea..00000000 --- a/openapi/paths/customers/customers_internal_accounts.yaml +++ /dev/null @@ -1,88 +0,0 @@ -get: - summary: List Customer internal accounts - description: | - Retrieve a list of internal accounts with optional filtering parameters. Returns all - internal accounts that match the specified filters. If no filters are provided, returns all internal accounts - (paginated). - - Internal accounts are created automatically when a customer is created based on the platform configuration. - operationId: listCustomerInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by internal accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: ../../components/schemas/customers/InternalAccount.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of customers matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/customers/customers_kyc_link.yaml b/openapi/paths/customers/customers_kyc_link.yaml deleted file mode 100644 index 809b4c50..00000000 --- a/openapi/paths/customers/customers_kyc_link.yaml +++ /dev/null @@ -1,54 +0,0 @@ -parameters: - - name: redirectUri - in: query - description: An optional uri a customer will be redirected to after completing the hosted KYC flow - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: The platform id of the customer to onboard - required: true - schema: - type: string -get: - summary: Get a KYC link for onboarding a customer - description: Generate a hosted KYC link to onboard a customer - operationId: getKycLinkForCustomer - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - properties: - kycUrl: - type: string - description: A hosted KYC link for your customer to complete KYC - example: "https://example.com/redirect" - platformCustomerId: - type: string - description: The platform id of the customer to onboard - example: 019542f5-b3e7-1d02-0000-000000000001 - customerId: - type: string - description: The customer id of the newly created customer on the system - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - diff --git a/openapi/paths/customers/customers_{customerId}.yaml b/openapi/paths/customers/customers_{customerId}.yaml deleted file mode 100644 index d2af2721..00000000 --- a/openapi/paths/customers/customers_{customerId}.yaml +++ /dev/null @@ -1,178 +0,0 @@ -parameters: - - name: customerId - in: path - description: System-generated unique customer identifier - required: true - schema: - type: string -get: - summary: Get customer by ID - description: Retrieve a customer by their system-generated ID - operationId: getCustomerById - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: ../../components/schemas/customers/IndividualCustomer.yaml - - title: Business Customer - $ref: ../../components/schemas/customers/BusinessCustomer.yaml - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ../../components/schemas/customers/IndividualCustomer.yaml - BUSINESS: ../../components/schemas/customers/BusinessCustomer.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Customer not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml -patch: - summary: Update customer by ID - description: Update a customer's metadata by their system-generated ID - operationId: updateCustomerById - tags: - - Customers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/customers/CustomerUpdateRequestOneOf.yaml - examples: - individualUpdate: - summary: Update individual customer example - value: - customerType: INDIVIDUAL - fullName: John Smith - birthDate: '1985-06-15' - address: - line1: 456 Market St - city: San Francisco - state: CA - postalCode: '94103' - country: US - businessUpdate: - summary: Update business customer example - value: - customerType: BUSINESS - businessInfo: - legalName: New Tech Solutions LLC - registrationNumber: BRN-987654321 - taxId: EIN-123456789 - address: - line1: 100 Technology Parkway - city: Palo Alto - state: CA - postalCode: '94304' - country: US - responses: - '200': - description: Customer updated successfully - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: ../../components/schemas/customers/IndividualCustomer.yaml - - title: Business Customer - $ref: ../../components/schemas/customers/BusinessCustomer.yaml - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ../../components/schemas/customers/IndividualCustomer.yaml - BUSINESS: ../../components/schemas/customers/BusinessCustomer.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Customer not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml -delete: - summary: Delete customer by ID - description: Delete a customer by their system-generated ID - operationId: deleteCustomerById - tags: - - Customers - security: - - BasicAuth: [] - responses: - '200': - description: Customer deleted successfully - content: - application/json: - schema: - oneOf: - - title: Individual Customer - $ref: ../../components/schemas/customers/IndividualCustomer.yaml - - title: Business Customer - $ref: ../../components/schemas/customers/BusinessCustomer.yaml - discriminator: - propertyName: customerType - mapping: - INDIVIDUAL: ../../components/schemas/customers/IndividualCustomer.yaml - BUSINESS: ../../components/schemas/customers/BusinessCustomer.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Customer not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '410': - description: Customer deleted already - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error410.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/internal-accounts/internal_accounts.yaml b/openapi/paths/internal-accounts/internal_accounts.yaml deleted file mode 100644 index 94b3334a..00000000 --- a/openapi/paths/internal-accounts/internal_accounts.yaml +++ /dev/null @@ -1,88 +0,0 @@ -get: - summary: Get all internal accounts - description: | - Retrieve a list of internal accounts with optional filtering parameters. Returns all - internal accounts that match the specified filters. If no filters are provided, returns all internal accounts - (paginated). - - Internal accounts are created automatically when a customer is created based on the platform configuration. - operationId: listInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - - name: customerId - in: query - description: Filter by internal accounts associated with a specific customer - required: false - schema: - type: string - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: ../../components/schemas/customers/InternalAccount.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of customers matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/invitations/invitations.yaml b/openapi/paths/invitations/invitations.yaml deleted file mode 100644 index 74224daa..00000000 --- a/openapi/paths/invitations/invitations.yaml +++ /dev/null @@ -1,75 +0,0 @@ -post: - summary: Create an UMA invitation - description: | - Create an UMA invitation from a given platform customer. - operationId: createInvitation - tags: - - Invitations - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - inviterUma - properties: - inviterUma: - type: string - description: The UMA address of the customer creating the invitation - example: $inviter@uma.domain - firstName: - type: string - description: First name of the invitee to show as part of the invite - example: Alice - amountToSend: - description: > - An amount to send (in the smallest unit of the customer's currency) - to the invitee when the invitation is claimed. - - This is optional and if not provided, the invitee will not - receive any amount. Note that the actual sending of - - the amount must be done by the inviter platform once the - INVITATION_CLAIMED webhook is received. If the inviter - - platform either does not send the payment or the payment fails, - the invitee will not receive this amount. This - - field is primarily used for display purposes on the claiming - side of the invitation. - type: integer - format: int64 - example: 12550 - expiresAt: - type: string - format: date-time - description: When the invitation expires (if at all) - example: '2025-09-01T14:30:00Z' - responses: - '201': - description: Invitation created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/invitations/UmaInvitation.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/invitations/invitations_{invitationCode}.yaml b/openapi/paths/invitations/invitations_{invitationCode}.yaml deleted file mode 100644 index 60be3c5d..00000000 --- a/openapi/paths/invitations/invitations_{invitationCode}.yaml +++ /dev/null @@ -1,41 +0,0 @@ -get: - summary: Get an UMA invitation by code - description: | - Retrieve details about an UMA invitation by its invitation code. - operationId: getInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to get - required: true - schema: - type: string - responses: - '200': - description: Invitation retrieved successfully - content: - application/json: - schema: - $ref: ../../components/schemas/invitations/UmaInvitation.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/invitations/invitations_{invitationCode}_cancel.yaml b/openapi/paths/invitations/invitations_{invitationCode}_cancel.yaml deleted file mode 100644 index 1e2f3195..00000000 --- a/openapi/paths/invitations/invitations_{invitationCode}_cancel.yaml +++ /dev/null @@ -1,71 +0,0 @@ -post: - summary: Cancel an UMA invitation - description: > - Cancel a pending UMA invitation. Only the inviter or platform can cancel an - invitation. - - - When an invitation is cancelled: - - 1. The invitation status changes from PENDING to CANCELLED - - 2. The invitation can no longer be claimed - - 3. The invitation URL will show as cancelled when accessed - - - Only pending invitations can be cancelled. Attempting to cancel an - invitation - - that is already claimed, expired, or cancelled will result in an error. - operationId: cancelInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to cancel - required: true - schema: - type: string - responses: - '200': - description: Invitation cancelled successfully - content: - application/json: - schema: - $ref: ../../components/schemas/invitations/UmaInvitation.yaml - '400': - description: >- - Bad request - Invitation cannot be cancelled (already claimed, expired, - or cancelled) - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '403': - description: Forbidden - Only the platform which created the invitation can cancel it - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error403.yaml - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/invitations/invitations_{invitationCode}_claim.yaml b/openapi/paths/invitations/invitations_{invitationCode}_claim.yaml deleted file mode 100644 index 2e3eb9f2..00000000 --- a/openapi/paths/invitations/invitations_{invitationCode}_claim.yaml +++ /dev/null @@ -1,74 +0,0 @@ -post: - summary: Claim an UMA invitation - description: > - Claim an UMA invitation by associating it with an invitee UMA address. - - - When an invitation is successfully claimed: - - 1. The invitation status changes from PENDING to CLAIMED - - 2. The invitee UMA address is associated with the invitation - - 3. An INVITATION_CLAIMED webhook is triggered to notify the platform that - created the invitation - - - This endpoint allows customers to accept invitations sent to them by other UMA - customers. - operationId: claimInvitation - tags: - - Invitations - security: - - BasicAuth: [] - parameters: - - name: invitationCode - in: path - description: The code of the invitation to claim - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - inviteeUma - properties: - inviteeUma: - type: string - description: The UMA address of the customer claiming the invitation - example: $invitee@uma.domain - responses: - '200': - description: Invitation claimed successfully - content: - application/json: - schema: - $ref: ../../components/schemas/invitations/UmaInvitation.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Invitation not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/plaid/plaid_callback_{plaid_link_token}.yaml b/openapi/paths/plaid/plaid_callback_{plaid_link_token}.yaml deleted file mode 100644 index 965607de..00000000 --- a/openapi/paths/plaid/plaid_callback_{plaid_link_token}.yaml +++ /dev/null @@ -1,64 +0,0 @@ -post: - summary: Submit Plaid public token - description: | - After the customer completes Plaid Link authentication, the platform should POST - the public_token to this callback URL (provided in the link token response). - - This will trigger asynchronous processing: - 1. Lightspark exchanges the public_token for an access_token with Plaid - 2. Lightspark retrieves and verifies the account details - 3. An external account is created - 4. A webhook notification is sent to the platform when complete - - operationId: submitPlaidPublicToken - tags: - - External Accounts - parameters: - - name: plaid_link_token - in: path - required: true - description: The Plaid link token from the link token response, used to identify the session - schema: - type: string - example: link-sandbox-abc123xyz-1234-5678 - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/plaid/PlaidCallbackRequest.yaml - example: - publicToken: public-sandbox-12345678-1234-1234-1234-123456789012 - accountId: plaid_account_id_123 - responses: - '202': - description: | - A pending external account resource will be created and returned while the Grid API asynchronously processes the Plaid public token. - content: - application/json: - schema: - $ref: ../../components/schemas/external_accounts/ExternalAccount.yaml - '400': - description: Bad request - Invalid public token or link token - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '404': - description: Link token not found or expired - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: Link token already used - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/plaid/plaid_link-tokens.yaml b/openapi/paths/plaid/plaid_link-tokens.yaml deleted file mode 100644 index 47951b2e..00000000 --- a/openapi/paths/plaid/plaid_link-tokens.yaml +++ /dev/null @@ -1,58 +0,0 @@ -post: - summary: Request Plaid Link token - description: | - Creates a Plaid Link token that can be used to initialize Plaid Link in your application. - The Link token is used to authenticate the customer and allow them to select their bank account. - - **Async Flow:** - 1. Platform calls this endpoint to get a link_token and callbackUrl - 2. Platform displays Plaid Link UI to the end customer using the link_token - 3. End customer authenticates with their bank and selects an account - 4. Plaid returns a public_token to the platform - 5. Platform POSTs the public_token to the callbackUrl - 6. Lightspark exchanges the public_token with Plaid and creates the external account asynchronously - 7. Platform receives a webhook notification when the external account is ready - operationId: createPlaidLinkToken - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/plaid/PlaidLinkTokenRequest.yaml - example: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - responses: - '200': - description: Link token created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/plaid/PlaidLinkTokenResponse.yaml - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Customer not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/platform/config.yaml b/openapi/paths/platform/config.yaml deleted file mode 100644 index 5a2147eb..00000000 --- a/openapi/paths/platform/config.yaml +++ /dev/null @@ -1,100 +0,0 @@ -get: - summary: Get platform configuration - description: Retrieve the current platform configuration - operationId: getPlatformConfig - tags: - - Platform Configuration - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: ../../components/schemas/config/PlatformConfig.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml -patch: - summary: Update platform configuration - description: Update the platform configuration settings - operationId: updatePlatformConfig - tags: - - Platform Configuration - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - umaDomain: - type: string - example: mycompany.com - webhookEndpoint: - type: string - example: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - type: array - items: - $ref: ../../components/schemas/config/PlatformCurrencyConfig.yaml - example: - umaDomain: mycompany.com - webhookEndpoint: https://api.mycompany.com/webhooks/uma - supportedCurrencies: - - currencyCode: USD - minAmount: 100 - maxAmount: 1000000 - enabledTransactionTypes: - - OUTGOING - - INCOMING - requiredCounterpartyFields: - - name: FULL_NAME - mandatory: true - - name: NATIONALITY - mandatory: true - - name: BIRTH_DATE - mandatory: true - responses: - '200': - description: Configuration updated successfully - content: - application/json: - schema: - $ref: ../../components/schemas/config/PlatformConfig.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - '501': - description: Not implemented - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error501.yaml diff --git a/openapi/paths/platform/platform_external_accounts.yaml b/openapi/paths/platform/platform_external_accounts.yaml deleted file mode 100644 index 9a6920d7..00000000 --- a/openapi/paths/platform/platform_external_accounts.yaml +++ /dev/null @@ -1,143 +0,0 @@ -get: - summary: List platform external accounts - description: | - Retrieve a list of all external accounts that belong to the platform, as opposed to an individual customer. - - These accounts are used for platform-wide operations such as receiving funds from external sources or managing platform-level payment destinations. - operationId: listPlatformExternalAccounts - tags: - - External Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - properties: - data: - type: array - description: List of external accounts matching the filter criteria - items: - $ref: ../../components/schemas/external_accounts/ExternalAccount.yaml - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - -post: - summary: Add a new platform external account - description: >- - Register a new external bank account for the platform. - - - **Sandbox Testing:** In sandbox mode, use these account number patterns to test different transfer scenarios. - These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. - For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - - Any other account number: Success (transfers complete normally) - operationId: createPlatformExternalAccount - tags: - - External Accounts - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/external_accounts/ExternalAccountCreateRequest.yaml - examples: - usBankAccount: - summary: Create external US bank account - value: - currency: USD - accountInfo: - accountType: US_ACCOUNT - accountNumber: "12345678901" - routingNumber: "123456789" - accountCategory: CHECKING - bankName: Chase Bank - platformAccountId: ext_acc_123456 - beneficiary: - beneficiaryType: INDIVIDUAL - fullName: John Doe - birthDate: "1990-01-15" - nationality: US - address: - line1: 123 Main Street - city: San Francisco - state: CA - postalCode: "94105" - country: US - sparkWallet: - summary: Create external Spark wallet - value: - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu" - responses: - '201': - description: External account created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/external_accounts/ExternalAccount.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '409': - description: Conflict - External account already exists - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/platform/platform_internal_accounts.yaml b/openapi/paths/platform/platform_internal_accounts.yaml deleted file mode 100644 index be56ce13..00000000 --- a/openapi/paths/platform/platform_internal_accounts.yaml +++ /dev/null @@ -1,51 +0,0 @@ -get: - summary: List platform internal accounts - description: | - Retrieve a list of all internal accounts that belong to the platform, as opposed to an individual customer. - - These accounts are created automatically when the platform is configured for each supported currency. They can be used for things like distributing bitcoin rewards to customers, or for other platform-wide purposes. - operationId: listPlatformInternalAccounts - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: currency - in: query - description: Filter by currency code - required: false - schema: - type: string - responses: - "200": - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - properties: - data: - type: array - description: List of internal accounts matching the filter criteria - items: - $ref: ../../components/schemas/customers/InternalAccount.yaml - "400": - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - "500": - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/quotes/quotes.yaml b/openapi/paths/quotes/quotes.yaml deleted file mode 100644 index 30c5e753..00000000 --- a/openapi/paths/quotes/quotes.yaml +++ /dev/null @@ -1,256 +0,0 @@ -post: - summary: Create a transfer quote - description: | - Generate a quote for a cross-currency transfer between any combination of accounts - and UMA addresses. This endpoint handles currency exchange and provides the necessary - instructions to execute the transfer. - - **Transfer Types Supported:** - - **Account to Account**: Transfer between internal/external accounts with currency exchange. - - **Account to UMA**: Transfer from an internal account to an UMA address. - - **UMA to Account or UMA to UMA**: This transfer type will only be funded by payment instructions, not from an internal account. - - **Key Features:** - - **Flexible Amount Locking**: Always specify whether you want to lock the sending amount or receiving amount - - **Currency Exchange**: Handles all cross-currency transfers with real-time exchange rates - - **Payment Instructions**: For UMA or customer ID sources, provides banking details needed for execution - - **Important:** If you are transferring funds in the same currency (no exchange required), - use the `/transfer-in` or `/transfer-out` endpoints instead. - - **Sandbox Testing:** When using the `externalAccountDetails` destination type in sandbox mode, use account number patterns ending in specific digits to test different scenarios. - These patterns should be used with the primary alias, address, or identifier of whatever account type you're testing. - For example, the US account number, a CLABE, an IBAN, a spark wallet address, etc. The failure patterns are: - - Account numbers ending in **002**: Insufficient funds (transfer-in will fail) - - Account numbers ending in **003**: Account closed/invalid (transfers will fail) - - Account numbers ending in **004**: Transfer rejected (bank rejects the transfer) - - Account numbers ending in **005**: Timeout/delayed failure (stays pending ~30s, then fails) - - Any other account number: Success (transfers complete normally) - operationId: createQuote - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/quotes/QuoteRequest.yaml - examples: - accountToAccount: - summary: Account to Account Transfer - value: - source: - accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: ExternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - currency: EUR - lockedCurrencySide: SENDING - lockedCurrencyAmount: 10000 - description: 'Transfer between accounts, either internal or external.' - accountToUma: - summary: Account to UMA Address Transfer - value: - lookupId: LookupRequest:019542f5-b3e7-1d02-0000-000000000009 - source: - accountId: InternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - umaAddress: $receiver@uma.domain.com - currency: EUR - lockedCurrencySide: SENDING - lockedCurrencyAmount: 1000 - description: 'Payment for invoice #1234' - realTimeFundingToSparkWallet: - summary: Real-time funding to Spark Wallet as an on-ramp flow. Immediate execution. - value: - source: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000009 - currency: USD - destination: - externalAccountDetails: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - currency: BTC - accountInfo: - accountType: SPARK_WALLET - address: spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu - lockedCurrencySide: RECEIVING - lockedCurrencyAmount: 10000 - immediatelyExecute: true - description: 'Bitcoin reward payout!' - responses: - '201': - description: | - Transfer quote created successfully. The response includes exchange rates, - fees, and transfer details. For transfers involving UMA addresses, payment - instructions are also included for execution through banking systems. - content: - application/json: - schema: - $ref: ../../components/schemas/quotes/Quote.yaml - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error412.yaml - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error424.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - '501': - description: Not implemented - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error501.yaml -get: - summary: List transfer quotes - description: | - Retrieve a list of transfer quotes with optional filtering parameters. Returns all - quotes that match the specified filters. If no filters are provided, returns all quotes - (paginated). - operationId: listQuotes - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: customerId - in: query - description: Filter by sending customer ID - required: false - schema: - type: string - - name: sendingAccountId - in: query - description: Filter by sending account ID - required: false - schema: - type: string - - name: receivingAccountId - in: query - description: Filter by receiving account ID - required: false - schema: - type: string - - name: sendingUmaAddress - in: query - description: Filter by sending UMA address - required: false - schema: - type: string - - name: receivingUmaAddress - in: query - description: Filter by receiving UMA address - required: false - schema: - type: string - - name: status - in: query - description: Filter by quote status - required: false - schema: - type: string - enum: - - PENDING - - PROCESSING - - COMPLETED - - FAILED - - EXPIRED - - name: createdAfter - in: query - description: Filter quotes created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter quotes created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of quotes matching the criteria - items: - $ref: ../../components/schemas/quotes/Quote.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of quotes matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml \ No newline at end of file diff --git a/openapi/paths/quotes/quotes_{quoteId}.yaml b/openapi/paths/quotes/quotes_{quoteId}.yaml deleted file mode 100644 index b242dae5..00000000 --- a/openapi/paths/quotes/quotes_{quoteId}.yaml +++ /dev/null @@ -1,46 +0,0 @@ -get: - summary: Get quote by ID - description: > - Retrieve a quote by its ID. If the quote has been settled, it will include - - the transaction ID. This allows clients to track the full lifecycle of a - payment - - from quote creation to settlement. - operationId: getQuoteById - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: quoteId - in: path - description: ID of the quote to retrieve - required: true - schema: - type: string - responses: - '200': - description: Quote retrieved successfully - content: - application/json: - schema: - $ref: ../../components/schemas/quotes/Quote.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Quote not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/quotes/quotes_{quoteId}_execute.yaml b/openapi/paths/quotes/quotes_{quoteId}_execute.yaml deleted file mode 100644 index cdb8b182..00000000 --- a/openapi/paths/quotes/quotes_{quoteId}_execute.yaml +++ /dev/null @@ -1,65 +0,0 @@ -post: - summary: Execute a quote - description: | - Execute a quote by its ID. This endpoint initiates the transfer between - the source and destination accounts. - - This endpoint can only be used for quotes with a `source` which is either an internal account, - or has direct pull functionality (e.g. ACH pull with an external account). - - Once executed, the quote cannot be cancelled and the transfer will be processed. - operationId: executeQuote - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: quoteId - in: path - required: true - description: The unique identifier of the quote to execute - schema: - type: string - example: Quote:019542f5-b3e7-1d02-0000-000000000001 - responses: - '200': - description: > - Quote confirmed successfully. The transfer has been initiated and - - the quote status has been updated. - content: - application/json: - schema: - $ref: ../../components/schemas/quotes/Quote.yaml - '400': - description: Bad request - Invalid quote ID or quote cannot be confirmed - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Quote not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: Conflict - Quote already confirmed, expired, or in invalid state - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml - - diff --git a/openapi/paths/receiver/external-account/receiver_external-account_{accountId}.yaml b/openapi/paths/receiver/external-account/receiver_external-account_{accountId}.yaml deleted file mode 100644 index 6752d8e3..00000000 --- a/openapi/paths/receiver/external-account/receiver_external-account_{accountId}.yaml +++ /dev/null @@ -1,86 +0,0 @@ -get: - summary: Look up an external account for payment - description: > - Lookup an external account by ID to determine supported currencies and - exchange rates. - - This endpoint helps platforms determine what currencies they can send to a - given external account, along with the current estimated exchange rates and - minimum and maximum amounts that can be sent. - operationId: lookupExternalAccount - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: accountId - in: path - description: System-generated ID of the external account - required: true - schema: - type: string - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - - name: senderUmaAddress - in: query - description: UMA address of the sender (optional if customerId is provided) - required: false - schema: - type: string - - name: customerId - in: query - description: System ID of the sender (optional if senderUmaAddress is provided) - required: false - schema: - type: string - responses: - '200': - description: Successful lookup - content: - application/json: - schema: - allOf: - - $ref: ../../../components/schemas/receiver/ReceiverLookupResponse.yaml - - type: object - required: - - accountId - properties: - accountId: - type: string - description: The external account ID that was looked up - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error401.yaml - '404': - description: External account not found - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error404.yaml - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error412.yaml - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error424.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/receiver/uma/receiver_uma_{receiverUmaAddress}.yaml b/openapi/paths/receiver/uma/receiver_uma_{receiverUmaAddress}.yaml deleted file mode 100644 index e3619b25..00000000 --- a/openapi/paths/receiver/uma/receiver_uma_{receiverUmaAddress}.yaml +++ /dev/null @@ -1,84 +0,0 @@ -get: - summary: Look up an UMA address for payment - description: > - Lookup a receiving UMA address to determine supported currencies and - exchange rates. - - This endpoint helps platforms determine what currencies they can send to a - given UMA address. - operationId: lookupUma - tags: - - Cross-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: receiverUmaAddress - in: path - description: UMA address of the intended recipient - required: true - schema: - type: string - - name: senderUmaAddress - in: query - description: UMA address of the sender (optional if customerId is provided) - required: false - schema: - type: string - - name: customerId - in: query - description: System ID of the sender (optional if senderUmaAddress is provided) - required: false - schema: - type: string - responses: - '200': - description: Successful lookup - content: - application/json: - schema: - allOf: - - $ref: ../../../components/schemas/receiver/ReceiverLookupResponse.yaml - - type: object - required: - - receiverUmaAddress - properties: - receiverUmaAddress: - type: string - description: The UMA address that was looked up - example: $receiver@uma.domain - '400': - description: Bad request - Missing or invalid parameters - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error401.yaml - '404': - description: UMA address not found - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error404.yaml - '412': - description: Counterparty doesn't support UMA version - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error412.yaml - '424': - description: Counterparty issue - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error424.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../../components/schemas/errors/Error500.yaml \ No newline at end of file diff --git a/openapi/paths/sandbox/sandbox_internal_accounts_{accountId}_fund.yaml b/openapi/paths/sandbox/sandbox_internal_accounts_{accountId}_fund.yaml deleted file mode 100644 index 8f6805d9..00000000 --- a/openapi/paths/sandbox/sandbox_internal_accounts_{accountId}_fund.yaml +++ /dev/null @@ -1,87 +0,0 @@ -post: - summary: Simulate funding an internal account - description: > - Simulate receiving funds into an internal account in the sandbox environment. - This is useful for testing scenarios where you need to add funds to a customer's - or platform's internal account without going through a real bank transfer or - following payment instructions. - - This endpoint is only for the sandbox environment and will fail for - production platforms/keys. - operationId: sandboxFundInternalAccount - tags: - - Sandbox - security: - - BasicAuth: [] - parameters: - - name: accountId - in: path - required: true - description: The ID of the internal account to fund - schema: - type: string - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - amount - properties: - amount: - type: integer - format: int64 - description: >- - Amount to add in the smallest unit of the account's currency (e.g., - cents for USD/EUR, satoshis for BTC) - exclusiveMinimum: 0 - maximum: 100000000000 - example: 100000 - examples: - fundUSDAccount: - summary: Fund USD internal account with $1,000 - value: - amount: 100000 - fundBTCAccount: - summary: Fund BTC internal account with 0.01 BTC - value: - amount: 1000000 - responses: - '200': - description: Internal account funded successfully - content: - application/json: - schema: - $ref: ../../components/schemas/customers/InternalAccount.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error403.yaml - '404': - description: Internal account not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/sandbox/sandbox_send.yaml b/openapi/paths/sandbox/sandbox_send.yaml deleted file mode 100644 index cf6d7998..00000000 --- a/openapi/paths/sandbox/sandbox_send.yaml +++ /dev/null @@ -1,75 +0,0 @@ -post: - summary: Simulate sending funds - description: > - Simulate sending funds to the bank account as instructed in the quote. - - This endpoint is only for the sandbox environment and will fail for - production platforms/keys. - operationId: sandboxSend - tags: - - Sandbox - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - quoteId - - currencyCode - properties: - quoteId: - type: string - description: The unique identifier of the quote - example: Quote:019542f5-b3e7-1d02-0000-000000000006 - currencyCode: - type: string - description: Currency code for the funds to be sent - example: USD - currencyAmount: - type: integer - format: int64 - description: >- - The amount to send in the smallest unit of the currency (eg. - cents). If not provided, the amount will be derived from the quote. - exclusiveMinimum: 0 - example: 1000 - responses: - '200': - description: Funds received successfully - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/OutgoingTransaction.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error403.yaml - '404': - description: Quote not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/sandbox/sandbox_uma_receive.yaml b/openapi/paths/sandbox/sandbox_uma_receive.yaml deleted file mode 100644 index da87f376..00000000 --- a/openapi/paths/sandbox/sandbox_uma_receive.yaml +++ /dev/null @@ -1,87 +0,0 @@ -post: - summary: Simulate payment send to test receiving an UMA payment - description: > - Simulate sending payment from an sandbox uma address to a platform customer to - test payment receive. - - This endpoint is only for the sandbox environment and will fail for - production platforms/keys. - operationId: sandboxReceive - tags: - - Sandbox - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - senderUmaAddress - - receivingCurrencyCode - - receivingCurrencyAmount - properties: - senderUmaAddress: - type: string - description: UMA address of the sender from the sandbox - example: $success.usd@sandbox.grid.uma.money - receiverUmaAddress: - type: string - description: UMA address of the receiver (optional if customerId is provided) - example: $receiver@uma.domain - customerId: - type: string - description: >- - System ID of the receiver (optional if receiverUmaAddress is - provided) - example: Customer:019542f5-b3e7-1d02-0000-000000000001 - receivingCurrencyCode: - type: string - description: The currency code for the receiving amount - example: USD - receivingCurrencyAmount: - type: integer - format: int64 - description: >- - The amount to be received in the smallest unit of the currency - (eg. cents) - exclusiveMinimum: 0 - example: 1000 - responses: - '200': - description: Payment triggered successfully - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/IncomingTransaction.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '403': - description: Forbidden - request was made with a production platform token - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error403.yaml - '404': - description: Sender or receiver not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/tokens/tokens.yaml b/openapi/paths/tokens/tokens.yaml deleted file mode 100644 index cb8735ef..00000000 --- a/openapi/paths/tokens/tokens.yaml +++ /dev/null @@ -1,163 +0,0 @@ -post: - summary: Create a new API token - description: Create a new API token to access the Grid APIs. - operationId: createToken - tags: - - API Tokens - security: - - BasicAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - title: tokenCreate - properties: - name: - type: string - description: Name of the token to help identify it - example: Sandbox read-only - permissions: - type: array - description: A list of permissions to grant to the token - items: - $ref: ../../components/schemas/tokens/Permission.yaml - required: - - name - - permissions - responses: - '201': - description: API token created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/tokens/ApiToken.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml -get: - summary: List tokens - description: > - Retrieve a list of API tokens with optional filtering parameters. Returns - all tokens that match - - the specified filters. If no filters are provided, returns all tokens - (paginated). - operationId: listTokens - tags: - - API Tokens - security: - - BasicAuth: [] - parameters: - - name: name - in: query - description: Filter by name of the token - required: false - schema: - type: string - - name: createdAfter - in: query - description: Filter customers created after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: createdBefore - in: query - description: Filter customers created before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedAfter - in: query - description: Filter customers updated after this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: updatedBefore - in: query - description: Filter customers updated before this timestamp (inclusive) - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of tokens matching the filter criteria - items: - $ref: ../../components/schemas/tokens/ApiToken.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of tokens matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/tokens/tokens_{tokenId}.yaml b/openapi/paths/tokens/tokens_{tokenId}.yaml deleted file mode 100644 index a662a23f..00000000 --- a/openapi/paths/tokens/tokens_{tokenId}.yaml +++ /dev/null @@ -1,75 +0,0 @@ -parameters: - - name: tokenId - in: path - description: System-generated unique token identifier - required: true - schema: - type: string -get: - summary: Get API token by ID - description: Retrieve an API token by their system-generated ID - operationId: getTokenById - tags: - - API Tokens - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: ../../components/schemas/tokens/ApiToken.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Token not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml -delete: - summary: Delete API token by ID - description: Delete an API token by their system-generated ID - operationId: deleteTokenById - tags: - - API Tokens - security: - - BasicAuth: [] - responses: - '204': - description: API token deleted successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Token not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/transactions/transactions.yaml b/openapi/paths/transactions/transactions.yaml deleted file mode 100644 index a2bf6fc3..00000000 --- a/openapi/paths/transactions/transactions.yaml +++ /dev/null @@ -1,140 +0,0 @@ -get: - summary: List transactions - description: | - Retrieve a paginated list of transactions with optional filtering. - The transactions can be filtered by customer ID, platform customer ID, UMA address, - date range, status, and transaction type. - operationId: listTransactions - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: customerId - in: query - description: Filter by system customer ID - required: false - schema: - type: string - - name: platformCustomerId - in: query - description: Filter by platform-specific customer ID - required: false - schema: - type: string - - name: senderAccountIdentifier - in: query - description: Filter by sender account identifier - required: false - schema: - type: string - - name: receiverAccountIdentifier - in: query - description: Filter by receiver account identifier - required: false - schema: - type: string - - name: status - in: query - description: Filter by transaction status - required: false - schema: - $ref: ../../components/schemas/transactions/TransactionStatus.yaml - - name: type - in: query - description: Filter by transaction type - required: false - schema: - $ref: ../../components/schemas/transactions/TransactionType.yaml - - name: reference - in: query - description: Filter by reference - required: false - schema: - type: string - - name: startDate - in: query - description: Filter by start date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: endDate - in: query - description: Filter by end date (inclusive) in ISO 8601 format - required: false - schema: - type: string - format: date-time - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: - - asc - - desc - default: desc - responses: - '200': - description: Successful operation - content: - application/json: - schema: - type: object - required: - - data - - hasMore - properties: - data: - type: array - description: List of transactions matching the criteria - items: - $ref: ../../components/schemas/transactions/Transaction.yaml - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: >- - Cursor to retrieve the next page of results (only present if - hasMore is true) - totalCount: - type: integer - description: >- - Total number of transactions matching the criteria (excluding - pagination) - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/transactions/transactions_{transactionId}.yaml b/openapi/paths/transactions/transactions_{transactionId}.yaml deleted file mode 100644 index aafdec0a..00000000 --- a/openapi/paths/transactions/transactions_{transactionId}.yaml +++ /dev/null @@ -1,40 +0,0 @@ -parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction - required: true - schema: - type: string -get: - summary: Get transaction by ID - description: Retrieve detailed information about a specific transaction. - operationId: getTransactionById - tags: - - Transactions - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/Transaction.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/transactions/transactions_{transactionId}_approve.yaml b/openapi/paths/transactions/transactions_{transactionId}_approve.yaml deleted file mode 100644 index d7eff6fd..00000000 --- a/openapi/paths/transactions/transactions_{transactionId}_approve.yaml +++ /dev/null @@ -1,73 +0,0 @@ -post: - summary: Approve a pending incoming payment - description: > - Approve a pending incoming payment that was previously acknowledged with a - 202 response. - - This endpoint allows platforms to asynchronously approve payments after - async processing. - operationId: approvePendingPayment - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction to approve - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - receiverCustomerInfo: - type: object - additionalProperties: true - description: >- - Information about the recipient, provided by the platform if - requested in the original webhook via - `requestedReceiverCustomerInfoFields`. - responses: - '200': - description: Payment approved successfully - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/IncomingTransaction.yaml - '400': - description: Bad request - Invalid parameters or payment cannot be approved - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: >- - Conflict - Payment is not in a pending state or has already been - processed or timed out. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/transactions/transactions_{transactionId}_reject.yaml b/openapi/paths/transactions/transactions_{transactionId}_reject.yaml deleted file mode 100644 index db0d6d49..00000000 --- a/openapi/paths/transactions/transactions_{transactionId}_reject.yaml +++ /dev/null @@ -1,72 +0,0 @@ -post: - summary: Reject a pending incoming payment - description: > - Reject a pending incoming payment that was previously acknowledged with a - 202 response. - - This endpoint allows platforms to asynchronously reject payments after - additional processing. - operationId: rejectPendingPayment - tags: - - Transactions - security: - - BasicAuth: [] - parameters: - - name: transactionId - in: path - description: Unique identifier of the transaction to reject - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - reason: - type: string - description: >- - Optional reason for rejecting the payment. This is just for - debugging purposes or can be used for a platform's own purposes. - example: RESTRICTED_JURISDICTION - responses: - '200': - description: Payment rejected successfully - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/IncomingTransaction.yaml - '400': - description: Bad request - Invalid parameters or payment cannot be rejected - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Transaction not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '409': - description: >- - Conflict - Payment is not in a pending state or has already been - processed or timed out. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/transfers/transfer_in.yaml b/openapi/paths/transfers/transfer_in.yaml deleted file mode 100644 index 682285e0..00000000 --- a/openapi/paths/transfers/transfer_in.yaml +++ /dev/null @@ -1,98 +0,0 @@ -post: - summary: Create a transfer-in request - description: > - Transfer funds from an external account to an internal account for a specific customer. - This endpoint should only be used for external account sources with pull functionality (e.g. ACH Pull). - Otherwise, use the paymentInstructions on the internal account to deposit funds. - operationId: createTransferIn - tags: - - Same-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: > - A unique identifier for the request. If the same key is sent multiple times, - the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - source - - destination - properties: - source: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an external account ID - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Source external account details - destination: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an internal account ID - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Destination internal account details - amount: - type: integer - format: int64 - description: >- - Amount in the smallest unit of the currency (e.g., cents for USD/EUR, - satoshis for BTC) - example: 12550 - examples: - transferIn: - summary: Transfer from external to internal account - value: - source: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - destination: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - amount: 12550 - responses: - '201': - description: Transfer-in request created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/Transaction.yaml - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Customer or account not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/transfers/transfer_out.yaml b/openapi/paths/transfers/transfer_out.yaml deleted file mode 100644 index d9785527..00000000 --- a/openapi/paths/transfers/transfer_out.yaml +++ /dev/null @@ -1,96 +0,0 @@ -post: - summary: Create a transfer-out request - description: > - Transfer funds from an internal account to an external account for a specific customer. - operationId: createTransferOut - tags: - - Same-Currency Transfers - security: - - BasicAuth: [] - parameters: - - name: Idempotency-Key - in: header - required: false - description: > - A unique identifier for the request. If the same key is sent multiple times, - the server will return the same response as the first request. - schema: - type: string - example: 550e8400-e29b-41d4-a716-446655440000 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - source - - destination - properties: - source: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an internal account ID - example: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - description: Source internal account details - destination: - type: object - required: - - accountId - properties: - accountId: - type: string - description: Reference to an external account ID - example: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - description: Destination external account details - amount: - type: integer - format: int64 - description: >- - Amount in the smallest unit of the currency (e.g., cents for USD/EUR, - satoshis for BTC) - example: 12550 - examples: - transferOut: - summary: Transfer from internal to external account - value: - source: - accountId: InternalAccount:a12dcbd6-dced-4ec4-b756-3c3a9ea3d123 - destination: - accountId: ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965 - amount: 12550 - responses: - '201': - description: Transfer-out request created successfully - content: - application/json: - schema: - $ref: ../../components/schemas/transactions/Transaction.yaml - '400': - description: Bad request - Invalid parameters - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '404': - description: Customer or account not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/uma_providers/uma_providers.yaml b/openapi/paths/uma_providers/uma_providers.yaml deleted file mode 100644 index 473e8b8c..00000000 --- a/openapi/paths/uma_providers/uma_providers.yaml +++ /dev/null @@ -1,86 +0,0 @@ -get: - summary: List available Counterparty Providers - description: | - Retrieve a list of available Counterparty Providers. The response includes basic information about each provider, such as its UMA address, name, and supported currencies. - operationId: getAvailableUmaProviders - tags: - - Available UMA Providers - parameters: - - in: query - name: countryCode - description: The alpha-2 representation of a country, as defined by the ISO 3166-1 standard. - required: false - schema: - type: string - example: "US" - - in: query - name: currencyCode - description: The ISO 4217 currency code to filter providers by supported currency. - required: false - schema: - type: string - example: "USD" - - in: query - name: hasBlockedProviders - description: Whether to include providers which are not on your allowlist in the response. By default the response will include blocked providers. - required: false - schema: - type: boolean - - name: limit - in: query - description: Maximum number of results to return (default 20, max 100) - required: false - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - - name: cursor - in: query - description: Cursor for pagination (returned from previous request) - required: false - schema: - type: string - - name: sortOrder - in: query - description: Order to sort results in - required: false - schema: - type: string - enum: [asc, desc] - default: desc - security: - - BasicAuth: [] - responses: - '200': - description: Successful operation - content: - application/json: - schema: - properties: - data: - description: List of available UMA Providers using Grid - type: array - items: - $ref: '../../components/schemas/uma_providers/UmaProvider.yaml' - hasMore: - type: boolean - description: Indicates if more results are available beyond this page - nextCursor: - type: string - description: Cursor to retrieve the next page of results (only present if hasMore is true) - totalCount: - type: integer - description: Total number of transactions matching the criteria (excluding pagination) - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/paths/webhooks/webhooks_test.yaml b/openapi/paths/webhooks/webhooks_test.yaml deleted file mode 100644 index 7186edc2..00000000 --- a/openapi/paths/webhooks/webhooks_test.yaml +++ /dev/null @@ -1,33 +0,0 @@ -post: - summary: Send a test webhook - description: Send a test webhook to the configured endpoint - operationId: sendTestWebhook - tags: - - Webhooks - security: - - BasicAuth: [] - responses: - '200': - description: Webhook delivered successfully - content: - application/json: - schema: - $ref: ../../components/schemas/webhooks/TestWebhookResponse.yaml - '400': - description: Bad request - Webhook delivery failed - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml diff --git a/openapi/webhooks/account-status.yaml b/openapi/webhooks/account-status.yaml deleted file mode 100644 index 02954cf7..00000000 --- a/openapi/webhooks/account-status.yaml +++ /dev/null @@ -1,92 +0,0 @@ -post: - summary: Account status notification webhook - description: > - Webhook that is called when the balance of an account changes - - This endpoint should be implemented by clients of the Grid API. - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by Grid. - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - - - ### Account status - - When the balance of an internal account changes, we will push a notification with information on the account, the new - balance, and who the account belongs to. - - - operationId: accountStatusWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/AccountStatusWebhook.yaml - examples: - balanceDecrease: - summary: A transaction just cleared a customer account and the balance has decreased - value: - account: - accountId: Account:019542f5-b3e7-1d02-0000-000000000005 - oldBalance: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - newBalance: - amount: 10000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 019542f5-b3e7-1d02-0000-000000000001 - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: ACCOUNT_STATUS - responses: - '200': - description: > - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/bulk-upload.yaml b/openapi/webhooks/bulk-upload.yaml deleted file mode 100644 index 81fddd3b..00000000 --- a/openapi/webhooks/bulk-upload.yaml +++ /dev/null @@ -1,100 +0,0 @@ -post: - summary: Bulk upload status webhook - description: > - Webhook that is called when a bulk customer upload job completes or fails. - - This endpoint should be implemented by clients of the Grid API. - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by Grid. - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - - - This webhook is sent when a bulk upload job completes or fails, providing - detailed information about the results. - operationId: bulkUploadWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '../components/schemas/customers/BulkUploadWebhookRequest.yaml' - examples: - completedUpload: - summary: Successful bulk upload completion - value: - bulkCustomerImportJob: - jobId: Job:019542f5-b3e7-1d02-0000-000000000006 - status: COMPLETED - progress: - total: 5000 - processed: 5000 - successful: 5000 - failed: 0 - errors: [] - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: BULK_UPLOAD - timestamp: '2025-08-15T14:32:00Z' - failedUpload: - summary: Failed bulk upload - value: - bulkCustomerImportJob: - jobId: Job:019542f5-b3e7-1d02-0000-000000000006 - status: FAILED - progress: - total: 5000 - processed: 5000 - successful: 0 - failed: 5000 - errors: - - correlationId: row_1 - error: - code: invalid_csv_format - message: Invalid CSV format - details: - reason: missing_required_column - column: umaAddress - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: BULK_UPLOAD - timestamp: '2025-08-15T14:32:00Z' - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/incoming-payment.yaml b/openapi/webhooks/incoming-payment.yaml deleted file mode 100644 index 78ad1b99..00000000 --- a/openapi/webhooks/incoming-payment.yaml +++ /dev/null @@ -1,189 +0,0 @@ -post: - summary: Incoming payment webhook and approval mechanism - description: > - Webhook that is called when an incoming payment is received by a customer's UMA - address. - - This endpoint should be implemented by clients of the Grid API. - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by Grid. - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - - - ### Payment Approval Flow - - When a transaction has `status: "PENDING"`, this webhook serves as an - approval mechanism: - - - 1. The client should check the `counterpartyInformation` against their - requirements - - 2. To APPROVE the payment synchronously, return a 200 OK response - - 3. To REJECT the payment, return a 403 Forbidden response with an Error - object - - 4. To request more information, return a 422 Unprocessable Entity with - specific missing fields - - 5. To process the payment asynchronously, return a 202 Accepted response and - then call the `/transactions/{transactionId}/approve` or - `/transactions/{transactionId}/reject` endpoint within 5 seconds. Note that - synchronous approval/rejection is preferred where possible. - - - The Grid system will proceed or cancel the payment based on your response. - - - For transactions with other statuses (COMPLETED, FAILED, REFUNDED), this - webhook is purely informational. - operationId: incomingPaymentWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/IncomingPaymentWebhook.yaml - examples: - pendingPayment: - summary: Pending payment example requiring approval - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: PENDING - type: INCOMING - senderUmaAddress: $sender@external.domain - receiverUmaAddress: $recipient@uma.domain - receivedAmount: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - reconciliationInstructions: - reference: REF-123456789 - counterpartyInformation: - FULL_NAME: John Sender - BIRTH_DATE: '1985-06-15' - NATIONALITY: US - requestedReceiverCustomerInfoFields: - - name: NATIONALITY - mandatory: true - - name: ADDRESS - mandatory: false - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: INCOMING_PAYMENT - incomingCompletedPayment: - summary: Completed payment notification - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: COMPLETED - type: INCOMING - senderUmaAddress: $sender@external.domain - receiverUmaAddress: $recipient@uma.domain - receivedAmount: - amount: 50000 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - settledAt: '2025-08-15T14:30:00Z' - createdAt: '2025-08-15T14:25:18Z' - description: Payment for services - reconciliationInstructions: - reference: REF-123456789 - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: INCOMING_PAYMENT - responses: - '200': - description: > - Webhook received successfully. - - For PENDING transactions, this indicates approval to proceed with the - payment. - - If `requestedReceiverCustomerInfoFields` were present in the webhook - request, the corresponding fields for the recipient must be included in - this response in the `receiverCustomerInfo` object. - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/IncomingPaymentWebhookResponse.yaml - - '202': - description: > - Webhook received and will be processed asynchronously. The synchronous - 200 response should be preferred where possible. This asycnhronous path - should only be used in - - cases where the platform's architecture requires async (but still very - quick) processing before approving or rejecting the payment. - - The platform must call the `/transactions/{transactionId}/approve` or - `/transactions/{transactionId}/reject` endpoint to approve or reject the - payment within 5 seconds or the payment will be automatically rejected. - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '403': - description: | - Forbidden - Payment rejected by the client. - Only applicable for PENDING transactions. - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/IncomingPaymentWebhookForbiddenResponse.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml - '422': - description: | - Unprocessable Entity - Additional counterparty information required. - Only applicable for PENDING transactions. - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/IncomingPaymentWebhookUnprocessableResponse.yaml diff --git a/openapi/webhooks/invitation-claimed.yaml b/openapi/webhooks/invitation-claimed.yaml deleted file mode 100644 index da81b05a..00000000 --- a/openapi/webhooks/invitation-claimed.yaml +++ /dev/null @@ -1,94 +0,0 @@ -post: - summary: Invitation claimed webhook - description: > - Webhook that is called when an invitation is claimed by a customer. - - This endpoint should be implemented by platform clients of the Grid API. - - - When a customer claims an invitation, this webhook is triggered to notify the - platform that: - - 1. The invitation has been successfully claimed - - 2. The invitee UMA address is now associated with the invitation - - 3. The invitation status has changed from PENDING to CLAIMED - - - This allows platforms to: - - - Track invitation usage and conversion rates - - - Trigger onboarding flows for new customers who joined via invitation - - - Apply referral bonuses or rewards to the inviter - - - Update their UI to reflect the claimed status - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by Grid. - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - operationId: invitationClaimedWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '../components/schemas/webhooks/InvitationClaimedWebhook.yaml' - examples: - claimedInvitation: - summary: Invitation claimed notification - value: - invitation: - code: 019542f5 - createdAt: '2025-09-01T14:30:00Z' - claimedAt: '2025-09-01T15:45:00Z' - inviterUma: $inviter@uma.domain - inviteeUma: $invitee@uma.domain - status: CLAIMED - url: https://uma.me/i/019542f5 - timestamp: '2025-09-01T15:45:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000008 - type: INVITATION_CLAIMED - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/kyc-status.yaml b/openapi/webhooks/kyc-status.yaml deleted file mode 100644 index 92b243f2..00000000 --- a/openapi/webhooks/kyc-status.yaml +++ /dev/null @@ -1,85 +0,0 @@ -post: - summary: KYC customer status change - description: > - Webhook that is called when the KYC status of a customer is updated. - - This endpoint should be implemented by clients of the Grid API. - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by Grid. - - To verify the signature: - - 1. Get the Grid API public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - - - ### KYC/B Flow - - This webhook is triggered when KYC/B has reached a decision on a customer. Generally most customers will finish KYC within a few minutes. - Others might be rejected because of incorrect data passed in or may have been flagged for manual review. - - The webhook will only trigger for final states. This will be APPROVED, REJECTED, EXPIRED, CANCELED, MANUALLY_APPROVED, MANUALLY_REJECTED. - - * APPROVED: The customer has been approved. - * REJECTED: The customer has been rejected after a KYC check. - * PENDING_REVIEW: KYC check is in progress. - * EXPIRED: KYC check has expired. This is generally because a customer did not submit all required information needed within a session. - * CANCELED: KYC check was canceled. - * MANUALLY_APPROVED: The customer was manually approved. - * MANUALLY_REJECTED: The customer was manually rejected. - * NOT_STARTED: KYC has not started on the customer. - operationId: kycStatusWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: ../components/schemas/webhooks/KycStatusWebhook.yaml - examples: - kycApprovedWebhook: - summary: When a customer KYC has been approved - value: - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - kycStatus: APPROVED - type: KYC_STATUS - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - responses: - '200': - description: > - Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/outgoing-payment.yaml b/openapi/webhooks/outgoing-payment.yaml deleted file mode 100644 index f9f62ec4..00000000 --- a/openapi/webhooks/outgoing-payment.yaml +++ /dev/null @@ -1,143 +0,0 @@ -post: - summary: Outgoing payment status webhook - description: > - Webhook that is called when an outgoing payment's status changes. - - This endpoint should be implemented by clients of the Grid API. - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by Grid. - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - - - This webhook is informational only and is sent when an outgoing payment - completes successfully or fails. - operationId: outgoingPaymentWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '../components/schemas/webhooks/OutgoingPaymentWebhook.yaml' - examples: - outgoingCompletedPayment: - summary: Completed outgoing payment - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: COMPLETED - type: OUTGOING - senderUmaAddress: $sender@uma.domain - receiverUmaAddress: $recipient@external.domain - sentAmount: - amount: 10550 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - receivedAmount: - amount: 9706 - currency: - code: EUR - name: Euro - symbol: € - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - settlementTime: '2025-08-15T14:30:00Z' - createdAt: '2025-08-15T14:25:18Z' - description: 'Payment for invoice #1234' - exchangeRate: 0.92 - quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006 - paymentInstructions: - - accountOrWalletInfo: - reference: UMA-Q12345-REF - accountType: US_ACCOUNT - accountNumber: 987654321 - routingNumber: 123456789 - accountCategory: CHECKING - bankName: Chase Bank - - accountOrWalletInfo: - accountType: SOLANA_WALLET - assetType: USDC - address: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: OUTGOING_PAYMENT - failedPayment: - summary: Failed outgoing payment - value: - transaction: - id: Transaction:019542f5-b3e7-1d02-0000-000000000005 - status: FAILED - type: OUTGOING - senderUmaAddress: $sender@uma.domain - receiverUmaAddress: $recipient@external.domain - sentAmount: - amount: 10550 - currency: - code: USD - name: United States Dollar - symbol: $ - decimals: 2 - customerId: Customer:019542f5-b3e7-1d02-0000-000000000001 - platformCustomerId: 18d3e5f7b4a9c2 - createdAt: '2025-08-15T14:25:18Z' - quoteId: Quote:019542f5-b3e7-1d02-0000-000000000006 - paymentInstructions: - - accountOrWalletInfo: - reference: UMA-Q12345-REF - accountType: US_ACCOUNT - accountNumber: 987654321 - routingNumber: 123456789 - accountCategory: CHECKING - bankName: Chase Bank - - accountOrWalletInfo: - accountType: SOLANA_WALLET - assetType: USDC - address: 4Nd1m6Qkq7RfKuE5vQ9qP9Tn6H94Ueqb4xXHzsAbd8Wg - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000007 - type: OUTGOING_PAYMENT - responses: - '200': - description: Webhook received successfully - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml diff --git a/openapi/webhooks/test-webhook.yaml b/openapi/webhooks/test-webhook.yaml deleted file mode 100644 index 242cd832..00000000 --- a/openapi/webhooks/test-webhook.yaml +++ /dev/null @@ -1,73 +0,0 @@ -post: - summary: Test webhook for integration verification - description: > - Webhook that is sent once to verify your webhook endpoint is correctly set - up. - - This is sent when you configure or update your platform settings with a - webhook URL. - - - ### Authentication - - The webhook includes a signature in the `X-Grid-Signature` header that - allows you to verify that the webhook was sent by the Grid API. - - To verify the signature: - - 1. Get the Grid public key provided to you during integration - - 2. Decode the base64 signature from the header - - 3. Create a SHA-256 hash of the request body - - 4. Verify the signature using the public key and the hash - - - If the signature verification succeeds, the webhook is authentic. If not, it - should be rejected. - - - This webhook is purely for testing your endpoint integration and signature - verification. - operationId: testWebhook - tags: - - Webhooks - security: - - WebhookSignature: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '../components/schemas/webhooks/TestWebhookRequest.yaml' - examples: - testWebhook: - summary: Test webhook example - value: - timestamp: '2025-08-15T14:32:00Z' - webhookId: Webhook:019542f5-b3e7-1d02-0000-000000000001 - type: TEST - responses: - '200': - description: >- - Webhook received successfully. This confirms your webhook endpoint is - properly configured. - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error400.yaml - '401': - description: Unauthorized - Signature validation failed - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error401.yaml - '409': - description: Conflict - Webhook has already been processed (duplicate webhookId) - content: - application/json: - schema: - $ref: ../components/schemas/errors/Error409.yaml diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f5a8dead..00000000 --- a/package-lock.json +++ /dev/null @@ -1,15499 +0,0 @@ -{ - "name": "grid-api", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "grid-api", - "version": "1.0.0", - "license": "Apache-2.0", - "dependencies": { - "@redocly/cli": "^1.34.5", - "mint": "^4.2.105" - }, - "devDependencies": { - "markdownlint-cli": "^0.44.0", - "widdershins": "^4.0.1" - } - }, - "node_modules/@alcalzone/ansi-tokenize": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.0.tgz", - "integrity": "sha512-qI/5TaaaCZE4yeSZ83lu0+xi1r88JSxUjnH4OP/iZF7+KKZ75u3ee5isd0LxX+6N8U0npL61YrpbthILHB6BnA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@alcalzone/ansi-tokenize/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@alcalzone/ansi-tokenize/node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ark/schema": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.49.0.tgz", - "integrity": "sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw==", - "license": "MIT", - "dependencies": { - "@ark/util": "0.49.0" - } - }, - "node_modules/@ark/util": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.49.0.tgz", - "integrity": "sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA==", - "license": "MIT" - }, - "node_modules/@asyncapi/parser": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.4.0.tgz", - "integrity": "sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ==", - "license": "Apache-2.0", - "dependencies": { - "@asyncapi/specs": "^6.8.0", - "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", - "@stoplight/json": "3.21.0", - "@stoplight/json-ref-readers": "^1.2.2", - "@stoplight/json-ref-resolver": "^3.1.5", - "@stoplight/spectral-core": "^1.18.3", - "@stoplight/spectral-functions": "^1.7.2", - "@stoplight/spectral-parsers": "^1.0.2", - "@stoplight/spectral-ref-resolver": "^1.0.3", - "@stoplight/types": "^13.12.0", - "@types/json-schema": "^7.0.11", - "@types/urijs": "^1.19.19", - "ajv": "^8.17.1", - "ajv-errors": "^3.0.0", - "ajv-formats": "^2.1.1", - "avsc": "^5.7.5", - "js-yaml": "^4.1.0", - "jsonpath-plus": "^10.0.0", - "node-fetch": "2.6.7" - } - }, - "node_modules/@asyncapi/parser/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@asyncapi/parser/node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, - "node_modules/@asyncapi/parser/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@asyncapi/specs": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.10.0.tgz", - "integrity": "sha512-vB5oKLsdrLUORIZ5BXortZTlVyGWWMC1Nud/0LtgxQ3Yn2738HigAD6EVqScvpPsDUI/bcLVsYEXN4dtXQHVng==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.11" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", - "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "license": "MIT" - }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", - "license": "MIT" - }, - "node_modules/@exodus/schemasafe": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", - "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" - }, - "node_modules/@faker-js/faker": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz", - "integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==", - "license": "MIT", - "engines": { - "node": ">=14.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/@humanwhocodes/momoa": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz", - "integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz", - "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/checkbox/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/checkbox/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/confirm": { - "version": "5.1.16", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz", - "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", - "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/core/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/core/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/editor": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", - "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/external-editor": "^1.0.1", - "@inquirer/type": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz", - "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", - "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", - "license": "MIT", - "dependencies": { - "chardet": "^2.1.0", - "iconv-lite": "^0.6.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", - "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz", - "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz", - "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz", - "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/password/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/prompts": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.4.tgz", - "integrity": "sha512-MuxVZ1en1g5oGamXV3DWP89GEkdD54alcfhHd7InUW5BifAdKQEK9SLFa/5hlWbvuhMPlobF0WAx7Okq988Jxg==", - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.2.2", - "@inquirer/confirm": "^5.1.16", - "@inquirer/editor": "^4.2.18", - "@inquirer/expand": "^4.0.18", - "@inquirer/input": "^4.2.2", - "@inquirer/number": "^3.0.18", - "@inquirer/password": "^4.0.18", - "@inquirer/rawlist": "^4.1.6", - "@inquirer/search": "^3.1.1", - "@inquirer/select": "^4.3.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz", - "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz", - "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz", - "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/select/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@inquirer/type": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", - "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jsep-plugin/assignment": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", - "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", - "engines": { - "node": ">= 10.16.0" - }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" - } - }, - "node_modules/@jsep-plugin/regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", - "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", - "engines": { - "node": ">= 10.16.0" - }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" - } - }, - "node_modules/@jsep-plugin/ternary": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.4.tgz", - "integrity": "sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==", - "license": "MIT", - "engines": { - "node": ">= 10.16.0" - }, - "peerDependencies": { - "jsep": "^0.4.0||^1.0.0" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", - "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@mintlify/cli": { - "version": "4.0.709", - "resolved": "https://registry.npmjs.org/@mintlify/cli/-/cli-4.0.709.tgz", - "integrity": "sha512-TGgwKBDulCRxG1Z1PiAeTWw8unjAhX55Xea3LTBUF+CdWPxOP3QP2droYKqJeKNQ5UFBnRwIkTx0/bTxfWUOtQ==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/common": "1.0.519", - "@mintlify/link-rot": "3.0.656", - "@mintlify/models": "0.0.225", - "@mintlify/prebuild": "1.0.643", - "@mintlify/previewing": "4.0.692", - "@mintlify/validation": "0.1.459", - "chalk": "^5.2.0", - "detect-port": "^1.5.1", - "fs-extra": "^11.2.0", - "gray-matter": "^4.0.3", - "ink": "^6.0.1", - "inquirer": "^12.3.0", - "js-yaml": "^4.1.0", - "react": "^19.1.0", - "semver": "^7.7.2", - "yargs": "^17.6.0" - }, - "bin": { - "mint": "bin/index.js", - "mintlify": "bin/index.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/cli/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@mintlify/cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/cli/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/@mintlify/cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/common": { - "version": "1.0.519", - "resolved": "https://registry.npmjs.org/@mintlify/common/-/common-1.0.519.tgz", - "integrity": "sha512-pXBsJsTT7W2F8fUOKHycWlN/Oh8GfaWtvHyARr/HkXy+Kec27OFXW539kdEHKDqFFdacJUc/iYcGb4Ehqnghog==", - "license": "ISC", - "dependencies": { - "@asyncapi/parser": "^3.4.0", - "@mintlify/mdx": "^2.0.5", - "@mintlify/models": "0.0.225", - "@mintlify/openapi-parser": "^0.0.7", - "@mintlify/validation": "0.1.459", - "@sindresorhus/slugify": "^2.1.1", - "acorn": "^8.11.2", - "acorn-jsx": "^5.3.2", - "estree-util-to-js": "^2.0.0", - "estree-walker": "^3.0.3", - "gray-matter": "^4.0.3", - "hast-util-from-html": "^2.0.3", - "hast-util-to-html": "^9.0.4", - "hast-util-to-text": "^4.0.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "mdast": "^3.0.0", - "mdast-util-from-markdown": "^2.0.2", - "mdast-util-gfm": "^3.0.0", - "mdast-util-mdx": "^3.0.0", - "mdast-util-mdx-jsx": "^3.1.3", - "micromark-extension-gfm": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.1", - "micromark-extension-mdxjs": "^3.0.0", - "openapi-types": "^12.0.0", - "postcss": "^8.5.6", - "remark": "^15.0.1", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "remark-math": "^6.0.0", - "remark-mdx": "^3.1.0", - "remark-stringify": "^11.0.0", - "tailwindcss": "^3.4.4", - "unified": "^11.0.5", - "unist-builder": "^4.0.0", - "unist-util-map": "^4.0.0", - "unist-util-remove": "^4.0.0", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1", - "vfile": "^6.0.3" - } - }, - "node_modules/@mintlify/common/node_modules/@mintlify/mdx": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@mintlify/mdx/-/mdx-2.0.5.tgz", - "integrity": "sha512-9WpFDRFqET1ovHWooi+fvWJorvYgEf2BjG65R8tUjqjTWzWqBbNVpumpoAojNqi7i7m0NeZami2oAOxqCyoy4g==", - "license": "MIT", - "dependencies": { - "@shikijs/transformers": "^3.11.0", - "hast-util-to-string": "^3.0.1", - "mdast-util-mdx-jsx": "^3.2.0", - "next-mdx-remote-client": "^1.0.3", - "rehype-katex": "^7.0.1", - "remark-gfm": "^4.0.0", - "remark-math": "^6.0.0", - "remark-smartypants": "^3.0.2", - "shiki": "^3.11.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0" - }, - "peerDependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1" - } - }, - "node_modules/@mintlify/common/node_modules/next-mdx-remote-client": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/next-mdx-remote-client/-/next-mdx-remote-client-1.1.2.tgz", - "integrity": "sha512-LZJxBU420dTZsbWOrNYZXkahGJu8lNKxLTrQrZl4JUsKeFtp91yA78dHMTfOcp7UAud3txhM1tayyoKFq4tw7A==", - "license": "MPL 2.0", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@mdx-js/mdx": "^3.1.0", - "@mdx-js/react": "^3.1.0", - "remark-mdx-remove-esm": "^1.2.0", - "serialize-error": "^12.0.0", - "vfile": "^6.0.3", - "vfile-matter": "^5.0.1" - }, - "engines": { - "node": ">=18.18.0" - }, - "peerDependencies": { - "react": ">= 18.3.0 < 19.0.0", - "react-dom": ">= 18.3.0 < 19.0.0" - } - }, - "node_modules/@mintlify/common/node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/@mintlify/common/node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@mintlify/common/node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/@mintlify/common/node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/@mintlify/link-rot": { - "version": "3.0.656", - "resolved": "https://registry.npmjs.org/@mintlify/link-rot/-/link-rot-3.0.656.tgz", - "integrity": "sha512-Ov7eQwe+vj27qmzo2Uc9pey4KjNH8h1GbGawAOATrL7qtn4VGUsFthRWeuhDAQvJqoK7uf465El08y9Qyn4a9Q==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/common": "1.0.519", - "@mintlify/prebuild": "1.0.643", - "@mintlify/previewing": "4.0.692", - "@mintlify/validation": "0.1.459", - "fs-extra": "^11.1.0", - "unist-util-visit": "^4.1.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/link-rot/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/link-rot/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/link-rot/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/models": { - "version": "0.0.225", - "resolved": "https://registry.npmjs.org/@mintlify/models/-/models-0.0.225.tgz", - "integrity": "sha512-ICo0TWcPJU5tCLHkYqBm+TMnBLG+wmAwGUENh6H62faKecKQqyzIHujqoIzTjBpWDs4aEBwdB2zOWjHkjWHtjg==", - "license": "Elastic-2.0", - "dependencies": { - "axios": "^1.8.3", - "openapi-types": "^12.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/openapi-parser": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@mintlify/openapi-parser/-/openapi-parser-0.0.7.tgz", - "integrity": "sha512-3ecbkzPbsnkKVZJypVL0H5pCTR7a4iLv4cP7zbffzAwy+vpH70JmPxNVpPPP62yLrdZlfNcMxu5xKeT7fllgMg==", - "license": "MIT", - "dependencies": { - "ajv": "^8.17.1", - "ajv-draft-04": "^1.0.0", - "ajv-formats": "^3.0.1", - "jsonpointer": "^5.0.1", - "leven": "^4.0.0", - "yaml": "^2.4.5" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mintlify/openapi-parser/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@mintlify/openapi-parser/node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@mintlify/openapi-parser/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@mintlify/openapi-parser/node_modules/leven": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-4.0.0.tgz", - "integrity": "sha512-puehA3YKku3osqPlNuzGDUHq8WpwXupUg1V6NXdV38G+gr+gkBwFC8g1b/+YcIvp8gnqVIus+eJCH/eGsRmJNw==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mintlify/openapi-parser/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/@mintlify/prebuild": { - "version": "1.0.643", - "resolved": "https://registry.npmjs.org/@mintlify/prebuild/-/prebuild-1.0.643.tgz", - "integrity": "sha512-J+1l86YZ58/aRe1pTf3GAw92jEW0Xbdp2zSssD9hupsGY+vHojFgvTbT21oTBlhOyiu0wvBitDUDRPB/jksNkw==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/common": "1.0.519", - "@mintlify/openapi-parser": "^0.0.7", - "@mintlify/scraping": "4.0.378", - "@mintlify/validation": "0.1.459", - "chalk": "^5.3.0", - "favicons": "^7.2.0", - "fs-extra": "^11.1.0", - "gray-matter": "^4.0.3", - "js-yaml": "^4.1.0", - "mdast": "^3.0.0", - "openapi-types": "^12.0.0", - "unist-util-visit": "^4.1.1" - } - }, - "node_modules/@mintlify/prebuild/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@mintlify/prebuild/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/prebuild/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/prebuild/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/previewing": { - "version": "4.0.692", - "resolved": "https://registry.npmjs.org/@mintlify/previewing/-/previewing-4.0.692.tgz", - "integrity": "sha512-ebLNVcQyr5gNuRc0xWoinY7v2/zccrf8p+aDDG5AyEyn4SoGv1jEYD09yyslsorouak122GlFbMHj3QY3e5Xlw==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/common": "1.0.519", - "@mintlify/prebuild": "1.0.643", - "@mintlify/validation": "0.1.459", - "better-opn": "^3.0.2", - "chalk": "^5.1.0", - "chokidar": "^3.5.3", - "express": "^4.18.2", - "fs-extra": "^11.1.0", - "got": "^13.0.0", - "gray-matter": "^4.0.3", - "ink": "^6.0.1", - "ink-spinner": "^5.0.0", - "is-online": "^10.0.0", - "js-yaml": "^4.1.0", - "mdast": "^3.0.0", - "openapi-types": "^12.0.0", - "react": "^19.1.0", - "socket.io": "^4.7.2", - "tar": "^6.1.15", - "unist-util-visit": "^4.1.1", - "yargs": "^17.6.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/previewing/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@mintlify/previewing/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/previewing/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/previewing/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/previewing/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mintlify/previewing/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/@mintlify/previewing/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/previewing/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/scraping": { - "version": "4.0.378", - "resolved": "https://registry.npmjs.org/@mintlify/scraping/-/scraping-4.0.378.tgz", - "integrity": "sha512-sh2qkLiVVit/6yyfOjqCqSNsS3URMxX0W/j5hJaz0o4FNdygRFfQUjg6f9KyBK/382rv38zGd9zwR3kLfQG3SA==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/common": "1.0.519", - "@mintlify/openapi-parser": "^0.0.7", - "fs-extra": "^11.1.1", - "hast-util-to-mdast": "^10.1.0", - "js-yaml": "^4.1.0", - "mdast-util-mdx-jsx": "^3.1.3", - "neotraverse": "^0.6.18", - "puppeteer": "^22.14.0", - "rehype-parse": "^9.0.0", - "remark-gfm": "^4.0.0", - "remark-mdx": "^3.0.1", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.5", - "unist-util-visit": "^5.0.0", - "yargs": "^17.6.0", - "zod": "^3.20.6" - }, - "bin": { - "mintlify-scrape": "bin/cli.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/scraping/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/scraping/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/@mintlify/scraping/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/scraping/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@mintlify/validation": { - "version": "0.1.459", - "resolved": "https://registry.npmjs.org/@mintlify/validation/-/validation-0.1.459.tgz", - "integrity": "sha512-fZJKNP5Tks9EtPkpSvE4kLwQEV2r5HH5T2DLea5LUEag4X+ruL+F6caA+/plvfwNGP0x2O5eohebidnKEOVvRg==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/models": "0.0.225", - "arktype": "^2.1.20", - "lcm": "^0.0.3", - "lodash": "^4.17.21", - "openapi-types": "^12.0.0", - "zod": "^3.20.6", - "zod-to-json-schema": "^3.20.3" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@openapi-contrib/openapi-schema-to-json-schema": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@openapi-contrib/openapi-schema-to-json-schema/-/openapi-schema-to-json-schema-3.2.0.tgz", - "integrity": "sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.53.0.tgz", - "integrity": "sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw==", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.26.0.tgz", - "integrity": "sha512-HedpXXYzzbaoutw6DFLWLDket2FwLkLpil4hGCZ1xYEIMTcivdfwEOISgdbLEWyG3HW52gTq2V9mOVJrONgiwg==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.26.0.tgz", - "integrity": "sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ==", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.27.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.53.0.tgz", - "integrity": "sha512-m7F5ZTq+V9mKGWYpX8EnZ7NjoqAU7VemQ1E2HAG+W/u0wpY1x0OmbxAXfGKFHCspdJk8UKlwPGrpcB8nay3P8A==", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/otlp-exporter-base": "0.53.0", - "@opentelemetry/otlp-transformer": "0.53.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-trace-base": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.53.0.tgz", - "integrity": "sha512-UCWPreGQEhD6FjBaeDuXhiMf6kkBODF0ZQzrk/tuQcaVDJ+dDQ/xhJp192H9yWnKxVpEjFrSSLnpqmX4VwX+eA==", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/otlp-transformer": "0.53.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.53.0.tgz", - "integrity": "sha512-rM0sDA9HD8dluwuBxLetUmoqGJKSAbWenwD65KY9iZhUxdBHRLrIdrABfNDP7aiTjcgK8XFyTn5fhDz7N+W6DA==", - "dependencies": { - "@opentelemetry/api-logs": "0.53.0", - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-logs": "0.53.0", - "@opentelemetry/sdk-metrics": "1.26.0", - "@opentelemetry/sdk-trace-base": "1.26.0", - "protobufjs": "^7.3.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/propagator-b3": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.26.0.tgz", - "integrity": "sha512-vvVkQLQ/lGGyEy9GT8uFnI047pajSOVnZI2poJqVGD3nJ+B9sFGdlHNnQKophE3lHfnIH0pw2ubrCTjZCgIj+Q==", - "dependencies": { - "@opentelemetry/core": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.26.0.tgz", - "integrity": "sha512-DelFGkCdaxA1C/QA0Xilszfr0t4YbGd3DjxiCDPh34lfnFr+VkkrjV9S8ZTJvAzfdKERXhfOxIKBoGPJwoSz7Q==", - "dependencies": { - "@opentelemetry/core": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.26.0.tgz", - "integrity": "sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw==", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/semantic-conventions": "1.27.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.53.0.tgz", - "integrity": "sha512-dhSisnEgIj/vJZXZV6f6KcTnyLDx/VuQ6l3ejuZpMpPlh9S1qMHiZU9NMmOkVkwwHkMy3G6mEBwdP23vUZVr4g==", - "dependencies": { - "@opentelemetry/api-logs": "0.53.0", - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.26.0.tgz", - "integrity": "sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ==", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz", - "integrity": "sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==", - "dependencies": { - "@opentelemetry/core": "1.26.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/semantic-conventions": "1.27.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.26.0.tgz", - "integrity": "sha512-Fj5IVKrj0yeUwlewCRwzOVcr5avTuNnMHWf7GPc1t6WaT78J6CJyF3saZ/0RkZfdeNO8IcBl/bNcWMVZBMRW8Q==", - "dependencies": { - "@opentelemetry/context-async-hooks": "1.26.0", - "@opentelemetry/core": "1.26.0", - "@opentelemetry/propagator-b3": "1.26.0", - "@opentelemetry/propagator-jaeger": "1.26.0", - "@opentelemetry/sdk-trace-base": "1.26.0", - "semver": "^7.5.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", - "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@puppeteer/browsers": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", - "integrity": "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.3.5", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@puppeteer/browsers/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@redocly/ajv": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.3.tgz", - "integrity": "sha512-4P3iZse91TkBiY+Dx5DUgxQ9GXkVJf++cmI0MOyLDxV9b5MUBI4II6ES8zA5JCbO72nKAJxWrw4PUPW+YP3ZDQ==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js-replace": "^1.0.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@redocly/cli": { - "version": "1.34.5", - "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-1.34.5.tgz", - "integrity": "sha512-5IEwxs7SGP5KEXjBKLU8Ffdz9by/KqNSeBk6YUVQaGxMXK//uYlTJIPntgUXbo1KAGG2d2q2XF8y4iFz6qNeiw==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "1.9.0", - "@opentelemetry/exporter-trace-otlp-http": "0.53.0", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-trace-node": "1.26.0", - "@opentelemetry/semantic-conventions": "1.27.0", - "@redocly/config": "^0.22.0", - "@redocly/openapi-core": "1.34.5", - "@redocly/respect-core": "1.34.5", - "abort-controller": "^3.0.0", - "chokidar": "^3.5.1", - "colorette": "^1.2.0", - "core-js": "^3.32.1", - "dotenv": "16.4.7", - "form-data": "^4.0.4", - "get-port-please": "^3.0.1", - "glob": "^7.1.6", - "handlebars": "^4.7.6", - "mobx": "^6.0.4", - "pluralize": "^8.0.0", - "react": "^17.0.0 || ^18.2.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.2.0 || ^19.0.0", - "redoc": "2.5.0", - "semver": "^7.5.2", - "simple-websocket": "^9.0.0", - "styled-components": "^6.0.7", - "yargs": "17.0.1" - }, - "bin": { - "openapi": "bin/cli.js", - "redocly": "bin/cli.js" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/cli/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@redocly/cli/node_modules/yargs": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", - "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@redocly/cli/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@redocly/config": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.2.tgz", - "integrity": "sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==", - "license": "MIT" - }, - "node_modules/@redocly/openapi-core": { - "version": "1.34.5", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.5.tgz", - "integrity": "sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==", - "license": "MIT", - "dependencies": { - "@redocly/ajv": "^8.11.2", - "@redocly/config": "^0.22.0", - "colorette": "^1.2.0", - "https-proxy-agent": "^7.0.5", - "js-levenshtein": "^1.1.6", - "js-yaml": "^4.1.0", - "minimatch": "^5.0.1", - "pluralize": "^8.0.0", - "yaml-ast-parser": "0.0.43" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/openapi-core/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@redocly/openapi-core/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@redocly/respect-core": { - "version": "1.34.5", - "resolved": "https://registry.npmjs.org/@redocly/respect-core/-/respect-core-1.34.5.tgz", - "integrity": "sha512-GheC/g/QFztPe9UA9LamooSplQuy9pe0Yr8XGTqkz0ahivLDl7svoy/LSQNn1QH3XGtLKwFYMfTwFR2TAYyh5Q==", - "license": "MIT", - "dependencies": { - "@faker-js/faker": "^7.6.0", - "@redocly/ajv": "8.11.2", - "@redocly/openapi-core": "1.34.5", - "better-ajv-errors": "^1.2.0", - "colorette": "^2.0.20", - "concat-stream": "^2.0.0", - "cookie": "^0.7.2", - "dotenv": "16.4.7", - "form-data": "^4.0.4", - "jest-diff": "^29.3.1", - "jest-matcher-utils": "^29.3.1", - "js-yaml": "4.1.0", - "json-pointer": "^0.6.2", - "jsonpath-plus": "^10.0.6", - "open": "^10.1.0", - "openapi-sampler": "^1.6.1", - "outdent": "^0.8.0", - "set-cookie-parser": "^2.3.5", - "undici": "^6.21.1" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/respect-core/node_modules/@redocly/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js-replace": "^1.0.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@redocly/respect-core/node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT" - }, - "node_modules/@shikijs/core": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.12.2.tgz", - "integrity": "sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.12.2.tgz", - "integrity": "sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.3" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.12.2.tgz", - "integrity": "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.12.2.tgz", - "integrity": "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.12.2.tgz", - "integrity": "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@shikijs/transformers": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.12.2.tgz", - "integrity": "sha512-+z1aMq4N5RoNGY8i7qnTYmG2MBYzFmwkm/yOd6cjEI7OVzcldVvzQCfxU1YbIVgsyB0xHVc2jFe1JhgoXyUoSQ==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.12.2", - "@shikijs/types": "3.12.2" - } - }, - "node_modules/@shikijs/types": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.12.2.tgz", - "integrity": "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "license": "MIT" - }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sindresorhus/slugify": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", - "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", - "license": "MIT", - "dependencies": { - "@sindresorhus/transliterate": "^1.0.0", - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sindresorhus/slugify/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sindresorhus/transliterate": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", - "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sindresorhus/transliterate/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@stoplight/json": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.0.tgz", - "integrity": "sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/ordered-object-literal": "^1.0.3", - "@stoplight/path": "^1.3.2", - "@stoplight/types": "^13.6.0", - "jsonc-parser": "~2.2.1", - "lodash": "^4.17.21", - "safe-stable-stringify": "^1.1" - }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/@stoplight/json-ref-readers": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@stoplight/json-ref-readers/-/json-ref-readers-1.2.2.tgz", - "integrity": "sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==", - "license": "Apache-2.0", - "dependencies": { - "node-fetch": "^2.6.0", - "tslib": "^1.14.1" - }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/@stoplight/json-ref-resolver": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-3.1.6.tgz", - "integrity": "sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/json": "^3.21.0", - "@stoplight/path": "^1.3.2", - "@stoplight/types": "^12.3.0 || ^13.0.0", - "@types/urijs": "^1.19.19", - "dependency-graph": "~0.11.0", - "fast-memoize": "^2.5.2", - "immer": "^9.0.6", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "urijs": "^1.19.11" - }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/@stoplight/json-ref-resolver/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/json/node_modules/jsonc-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", - "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==", - "license": "MIT" - }, - "node_modules/@stoplight/ordered-object-literal": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", - "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/@stoplight/path": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz", - "integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/@stoplight/spectral-core": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.20.0.tgz", - "integrity": "sha512-5hBP81nCC1zn1hJXL/uxPNRKNcB+/pEIHgCjPRpl/w/qy9yC9ver04tw1W0l/PMiv0UeB5dYgozXVQ4j5a6QQQ==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/better-ajv-errors": "1.0.3", - "@stoplight/json": "~3.21.0", - "@stoplight/path": "1.3.2", - "@stoplight/spectral-parsers": "^1.0.0", - "@stoplight/spectral-ref-resolver": "^1.0.4", - "@stoplight/spectral-runtime": "^1.1.2", - "@stoplight/types": "~13.6.0", - "@types/es-aggregate-error": "^1.0.2", - "@types/json-schema": "^7.0.11", - "ajv": "^8.17.1", - "ajv-errors": "~3.0.0", - "ajv-formats": "~2.1.1", - "es-aggregate-error": "^1.0.7", - "jsonpath-plus": "^10.3.0", - "lodash": "~4.17.21", - "lodash.topath": "^4.5.2", - "minimatch": "3.1.2", - "nimma": "0.2.3", - "pony-cause": "^1.1.1", - "simple-eval": "1.0.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": "^16.20 || ^18.18 || >= 20.17" - } - }, - "node_modules/@stoplight/spectral-core/node_modules/@stoplight/better-ajv-errors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", - "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", - "license": "Apache-2.0", - "dependencies": { - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - }, - "engines": { - "node": "^12.20 || >= 14.13" - }, - "peerDependencies": { - "ajv": ">=8" - } - }, - "node_modules/@stoplight/spectral-core/node_modules/@stoplight/types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz", - "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } - }, - "node_modules/@stoplight/spectral-core/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@stoplight/spectral-core/node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, - "node_modules/@stoplight/spectral-core/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/spectral-formats": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.8.2.tgz", - "integrity": "sha512-c06HB+rOKfe7tuxg0IdKDEA5XnjL2vrn/m/OVIIxtINtBzphZrOgtRn7epQ5bQF5SWp84Ue7UJWaGgDwVngMFw==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/json": "^3.17.0", - "@stoplight/spectral-core": "^1.19.2", - "@types/json-schema": "^7.0.7", - "tslib": "^2.8.1" - }, - "engines": { - "node": "^16.20 || ^18.18 || >= 20.17" - } - }, - "node_modules/@stoplight/spectral-formats/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/spectral-functions": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.10.1.tgz", - "integrity": "sha512-obu8ZfoHxELOapfGsCJixKZXZcffjg+lSoNuttpmUFuDzVLT3VmH8QkPXfOGOL5Pz80BR35ClNAToDkdnYIURg==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/better-ajv-errors": "1.0.3", - "@stoplight/json": "^3.17.1", - "@stoplight/spectral-core": "^1.19.4", - "@stoplight/spectral-formats": "^1.8.1", - "@stoplight/spectral-runtime": "^1.1.2", - "ajv": "^8.17.1", - "ajv-draft-04": "~1.0.0", - "ajv-errors": "~3.0.0", - "ajv-formats": "~2.1.1", - "lodash": "~4.17.21", - "tslib": "^2.8.1" - }, - "engines": { - "node": "^16.20 || ^18.18 || >= 20.17" - } - }, - "node_modules/@stoplight/spectral-functions/node_modules/@stoplight/better-ajv-errors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", - "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==", - "license": "Apache-2.0", - "dependencies": { - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - }, - "engines": { - "node": "^12.20 || >= 14.13" - }, - "peerDependencies": { - "ajv": ">=8" - } - }, - "node_modules/@stoplight/spectral-functions/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@stoplight/spectral-functions/node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@stoplight/spectral-functions/node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, - "node_modules/@stoplight/spectral-functions/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/spectral-parsers": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.5.tgz", - "integrity": "sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/json": "~3.21.0", - "@stoplight/types": "^14.1.1", - "@stoplight/yaml": "~4.3.0", - "tslib": "^2.8.1" - }, - "engines": { - "node": "^16.20 || ^18.18 || >= 20.17" - } - }, - "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", - "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } - }, - "node_modules/@stoplight/spectral-parsers/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/spectral-ref-resolver": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.5.tgz", - "integrity": "sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/json-ref-readers": "1.2.2", - "@stoplight/json-ref-resolver": "~3.1.6", - "@stoplight/spectral-runtime": "^1.1.2", - "dependency-graph": "0.11.0", - "tslib": "^2.8.1" - }, - "engines": { - "node": "^16.20 || ^18.18 || >= 20.17" - } - }, - "node_modules/@stoplight/spectral-ref-resolver/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/spectral-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.4.tgz", - "integrity": "sha512-YHbhX3dqW0do6DhiPSgSGQzr6yQLlWybhKwWx0cqxjMwxej3TqLv3BXMfIUYFKKUqIwH4Q2mV8rrMM8qD2N0rQ==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/json": "^3.20.1", - "@stoplight/path": "^1.3.2", - "@stoplight/types": "^13.6.0", - "abort-controller": "^3.0.0", - "lodash": "^4.17.21", - "node-fetch": "^2.7.0", - "tslib": "^2.8.1" - }, - "engines": { - "node": "^16.20 || ^18.18 || >= 20.17" - } - }, - "node_modules/@stoplight/spectral-runtime/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@stoplight/types": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz", - "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } - }, - "node_modules/@stoplight/yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz", - "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==", - "license": "Apache-2.0", - "dependencies": { - "@stoplight/ordered-object-literal": "^1.0.5", - "@stoplight/types": "^14.1.1", - "@stoplight/yaml-ast-parser": "0.0.50", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=10.8" - } - }, - "node_modules/@stoplight/yaml-ast-parser": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz", - "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==", - "license": "Apache-2.0" - }, - "node_modules/@stoplight/yaml/node_modules/@stoplight/types": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", - "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } - }, - "node_modules/@stoplight/yaml/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/es-aggregate-error": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.6.tgz", - "integrity": "sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/katex": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", - "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", - "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "22.13.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", - "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/react": { - "version": "19.1.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", - "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", - "license": "MIT", - "peer": true, - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/stylis": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", - "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==", - "license": "MIT" - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true - }, - "node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" - }, - "node_modules/@types/urijs": { - "version": "1.19.25", - "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz", - "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==", - "license": "MIT" - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "license": "MIT", - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arktype": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.1.22.tgz", - "integrity": "sha512-xdzl6WcAhrdahvRRnXaNwsipCgHuNoLobRqhiP8RjnfL9Gp947abGlo68GAIyLtxbD+MLzNyH2YR4kEqioMmYQ==", - "license": "MIT", - "dependencies": { - "@ark/schema": "0.49.0", - "@ark/util": "0.49.0" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ast-types/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/auto-bind": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", - "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/avsc": { - "version": "5.7.9", - "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.7.9.tgz", - "integrity": "sha512-yOA4wFeI7ET3v32Di/sUybQ+ttP20JHSW3mxLuNGeO0uD6PPcvLrIQXSvy/rhJOWU5JrYh7U4OHplWMmtAtjMg==", - "license": "MIT", - "engines": { - "node": ">=0.11" - } - }, - "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0" - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/bare-events": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", - "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/bare-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.2.3.tgz", - "integrity": "sha512-1aGs5pRVLToMQ79elP+7cc0u0s/wXAzfBv/7hDloT7WFggLqECCas5qqPky7WHCFdsBH5WDq6sD4fAoz5sJbtA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/better-ajv-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-1.2.0.tgz", - "integrity": "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==", - "license": "Apache-2.0", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "@humanwhocodes/momoa": "^2.0.2", - "chalk": "^4.1.2", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0 < 4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "peerDependencies": { - "ajv": "4.11.8 - 8" - } - }, - "node_modules/better-opn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", - "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", - "license": "MIT", - "dependencies": { - "open": "^8.0.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/better-opn/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/better-opn/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/better-opn/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/better-opn/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/camelize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", - "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chardet": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", - "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/chromium-bidi": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.3.tgz", - "integrity": "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/chromium-bidi/node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, - "node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clean-stack/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "license": "MIT", - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "license": "MIT" - }, - "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/code-error-fragment": { - "version": "0.0.230", - "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", - "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/code-excerpt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", - "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", - "license": "MIT", - "dependencies": { - "convert-to-spaces": "^2.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-to-spaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", - "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/core-js": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", - "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/css-to-react-native": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", - "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", - "license": "MIT", - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decko": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decko/-/decko-1.2.0.tgz", - "integrity": "sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ==" - }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "license": "MIT", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.1312386", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", - "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", - "license": "BSD-3-Clause" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dns-socket": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-4.2.2.tgz", - "integrity": "sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==", - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.4" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dompurify": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", - "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } - }, - "node_modules/dot": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.3.tgz", - "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==", - "dev": true, - "engines": [ - "node >=0.2.6" - ], - "bin": { - "dottojs": "bin/dot-packer" - } - }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", - "dev": true - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-aggregate-error": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.14.tgz", - "integrity": "sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "globalthis": "^1.0.4", - "has-property-descriptors": "^1.0.2", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-toolkit": { - "version": "1.39.10", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz", - "integrity": "sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==", - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks" - ] - }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js/node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-visit/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-memoize": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", - "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "dependencies": { - "strnum": "^1.1.1" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/favicons": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/favicons/-/favicons-7.2.0.tgz", - "integrity": "sha512-k/2rVBRIRzOeom3wI9jBPaSEvoTSQEW4iM0EveBmBBKFxO8mSyyRWtDlfC3VnEfu0avmjrMzy8/ZFPSe6F71Hw==", - "license": "MIT", - "dependencies": { - "escape-html": "^1.0.3", - "sharp": "^0.33.1", - "xml2js": "^0.6.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/foreach": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", - "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/foreground-child/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "license": "MIT", - "engines": { - "node": ">= 14.17" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", - "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-readfile-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-2.0.1.tgz", - "integrity": "sha512-7+P9eOOMnkIOmtxrBWTzWOBQlE7Nz/cBx9EYTX5hm8DzmZ/Fj9YWeUY2O9G+Q8YblScd1hyEkcmNcZMDj5U8Ug==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2" - } - }, - "node_modules/fs-writefile-promise": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-writefile-promise/-/fs-writefile-promise-1.0.3.tgz", - "integrity": "sha512-yI+wDwj0FsgX7tyIQJR+EP60R64evMSixtGb9AzGWjJVKlF5tCet95KomfqGBg/aIAG1Dhd6wjCOQe5HbX/qLA==", - "dev": true, - "dependencies": { - "mkdirp-promise": "^1.0.0", - "pinkie-promise": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/fs-writefile-promise/node_modules/pinkie": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz", - "integrity": "sha512-VFVaU1ysKakao68ktZm76PIdOhvEfoNNRaGkyLln9Os7r0/MCxqHjHyBM7dT3pgTiBybqiPtpqKfpENwdBp50Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs-writefile-promise/node_modules/pinkie-promise": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", - "integrity": "sha512-5mvtVNse2Ml9zpFKkWBpGsTPwm3DKhs+c95prO/F6E7d6DN0FPqxs6LONpLNpyD7Iheb7QN4BbUoKJgo+DnkQA==", - "dev": true, - "dependencies": { - "pinkie": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gcd": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/gcd/-/gcd-0.0.1.tgz", - "integrity": "sha512-VNx3UEGr+ILJTiMs1+xc5SX1cMgJCrXezKPa003APUWNqQqaF6n25W8VcR7nHN6yRWbvvUTwCpZCFJeWC2kXlw==", - "license": "MIT" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.1.tgz", - "integrity": "sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "node_modules/get-port-please": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", - "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==" - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-uri": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", - "license": "MIT", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", - "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-embedded": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", - "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-dom": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", - "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", - "license": "ISC", - "dependencies": { - "@types/hast": "^3.0.0", - "hastscript": "^9.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", - "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-dom": "^5.0.0", - "hast-util-from-html": "^2.0.0", - "unist-util-remove-position": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/hast-util-has-property": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", - "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-body-ok-link": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", - "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-minify-whitespace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", - "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-phrasing": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", - "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-embedded": "^3.0.0", - "hast-util-has-property": "^3.0.0", - "hast-util-is-body-ok-link": "^3.0.0", - "hast-util-is-element": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/hast-util-to-mdast": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/hast-util-to-mdast/-/hast-util-to-mdast-10.1.2.tgz", - "integrity": "sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-phrasing": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "hast-util-to-text": "^4.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-minify-whitespace": "^6.0.0", - "trim-trailing-lines": "^2.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-string": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", - "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/highlightjs": { - "version": "9.16.2", - "resolved": "https://registry.npmjs.org/highlightjs/-/highlightjs-9.16.2.tgz", - "integrity": "sha512-FK1vmMj8BbEipEy8DLIvp71t5UsC7n2D6En/UfM/91PCwmOpj6f2iu0Y0coRC62KSRHHC+dquM2xMULV/X7NFg==", - "deprecated": "Use the 'highlight.js' package instead https://npm.im/highlight.js", - "dev": true - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/http2-client": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", - "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" - }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/httpsnippet": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/httpsnippet/-/httpsnippet-1.25.0.tgz", - "integrity": "sha512-jobE6S923cLuf5BPG6Jf+oLBRkPzv2RPp0dwOHcWwj/t9FwV/t9hyZ46kpT3Q5DHn9iFNmGhrcmmFUBqyjoTQg==", - "dev": true, - "dependencies": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "debug": "^2.2.0", - "event-stream": "3.3.4", - "form-data": "3.0.0", - "fs-readfile-promise": "^2.0.1", - "fs-writefile-promise": "^1.0.3", - "har-validator": "^5.0.0", - "pinkie-promise": "^2.0.0", - "stringify-object": "^3.3.0" - }, - "bin": { - "httpsnippet": "bin/httpsnippet" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/httpsnippet/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/httpsnippet/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/httpsnippet/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/httpsnippet/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/httpsnippet/node_modules/form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/httpsnippet/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/httpsnippet/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/httpsnippet/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", - "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ink": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ink/-/ink-6.3.0.tgz", - "integrity": "sha512-2CbJAa7XeziZYe6pDS5RVLirRY28iSGMQuEV8jRU5NQsONQNfcR/BZHHc9vkMg2lGYTHTM2pskxC1YmY28p6bQ==", - "license": "MIT", - "dependencies": { - "@alcalzone/ansi-tokenize": "^0.2.0", - "ansi-escapes": "^7.0.0", - "ansi-styles": "^6.2.1", - "auto-bind": "^5.0.1", - "chalk": "^5.6.0", - "cli-boxes": "^3.0.0", - "cli-cursor": "^4.0.0", - "cli-truncate": "^4.0.0", - "code-excerpt": "^4.0.0", - "es-toolkit": "^1.39.10", - "indent-string": "^5.0.0", - "is-in-ci": "^2.0.0", - "patch-console": "^2.0.0", - "react-reconciler": "^0.32.0", - "signal-exit": "^3.0.7", - "slice-ansi": "^7.1.0", - "stack-utils": "^2.0.6", - "string-width": "^7.2.0", - "type-fest": "^4.27.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0", - "ws": "^8.18.0", - "yoga-layout": "~3.2.1" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@types/react": ">=19.0.0", - "react": ">=19.0.0", - "react-devtools-core": "^4.19.1" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react-devtools-core": { - "optional": true - } - } - }, - "node_modules/ink-spinner": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ink-spinner/-/ink-spinner-5.0.0.tgz", - "integrity": "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==", - "license": "MIT", - "dependencies": { - "cli-spinners": "^2.7.0" - }, - "engines": { - "node": ">=14.16" - }, - "peerDependencies": { - "ink": ">=4.0.0", - "react": ">=18.0.0" - } - }, - "node_modules/ink/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ink/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ink/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ink/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "license": "MIT" - }, - "node_modules/ink/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ink/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/ink/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/ink/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "license": "MIT" - }, - "node_modules/inquirer": { - "version": "12.9.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.4.tgz", - "integrity": "sha512-5bV3LOgLtMAiJq1QpaUddfRrvaX59wiMYppS7z2jNRSQ64acI0yqx7WMxWhgymenSXOyD657g9tlsTjqGYM8sg==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/prompts": "^7.8.4", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "mute-stream": "^2.0.0", - "run-async": "^4.0.5", - "rxjs": "^7.8.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/inquirer/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-in-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz", - "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==", - "license": "MIT", - "bin": { - "is-in-ci": "cli.js" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-ip": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", - "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", - "license": "MIT", - "dependencies": { - "ip-regex": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-online": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/is-online/-/is-online-10.0.0.tgz", - "integrity": "sha512-WCPdKwNDjXJJmUubf2VHLMDBkUZEtuOvpXUfUnUFbEnM6In9ByiScL4f4jKACz/fsb2qDkesFerW3snf/AYz3A==", - "license": "MIT", - "dependencies": { - "got": "^12.1.0", - "p-any": "^4.0.0", - "p-timeout": "^5.1.0", - "public-ip": "^5.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-online/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-online/node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jgexml": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/jgexml/-/jgexml-0.4.4.tgz", - "integrity": "sha512-j0AzSWT7LXy3s3i1cdv5NZxUtscocwiBxgOLiEBfitCehm8STdXVrcOlbAWsJFLCq1elZYpQlGqA9k8Z+n9iJA==", - "dev": true, - "bin": { - "json2xml": "cli/json2xml.js", - "xml2json": "cli/xml2json.js", - "xsd2json": "cli/xsd2json.js" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/jsep": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", - "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, - "node_modules/json-pointer": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", - "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", - "dependencies": { - "foreach": "^2.0.4" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-to-ast": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", - "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", - "dev": true, - "dependencies": { - "code-error-fragment": "0.0.230", - "grapheme-splitter": "^1.0.4" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonpath-plus": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", - "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", - "dependencies": { - "@jsep-plugin/assignment": "^1.3.0", - "@jsep-plugin/regex": "^1.0.4", - "jsep": "^1.4.0" - }, - "bin": { - "jsonpath": "bin/jsonpath-cli.js", - "jsonpath-plus": "bin/jsonpath-cli.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/katex": { - "version": "0.16.21", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", - "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "dependencies": { - "invert-kv": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lcm": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/lcm/-/lcm-0.0.3.tgz", - "integrity": "sha512-TB+ZjoillV6B26Vspf9l2L/vKaRY/4ep3hahcyVkCGFgsTNRUQdc24bQeNFiZeoxH0vr5+7SfNRMQuPHv/1IrQ==", - "license": "MIT", - "dependencies": { - "gcd": "^0.0.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "dev": true, - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.topath": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", - "integrity": "sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==", - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", - "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "license": "MIT" - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", - "dev": true - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "license": "MIT" - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-emoji": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", - "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==", - "dev": true - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/markdownlint": { - "version": "0.37.4", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.37.4.tgz", - "integrity": "sha512-u00joA/syf3VhWh6/ybVFkib5Zpj2e5KB/cfCei8fkSRuums6nyisTWGqjTWIOFoFwuXoTBQQiqlB4qFKp8ncQ==", - "dev": true, - "dependencies": { - "markdown-it": "14.1.0", - "micromark": "4.0.1", - "micromark-core-commonmark": "2.0.2", - "micromark-extension-directive": "3.0.2", - "micromark-extension-gfm-autolink-literal": "2.1.0", - "micromark-extension-gfm-footnote": "2.1.0", - "micromark-extension-gfm-table": "2.1.0", - "micromark-extension-math": "3.1.0", - "micromark-util-types": "2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, - "node_modules/markdownlint-cli": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.44.0.tgz", - "integrity": "sha512-ZJTAONlvF9NkrIBltCdW15DxN9UTbPiKMEqAh2EU2gwIFlrCMavyCEPPO121cqfYOrLUJWW8/XKWongstmmTeQ==", - "dev": true, - "dependencies": { - "commander": "~13.1.0", - "glob": "~10.4.5", - "ignore": "~7.0.3", - "js-yaml": "~4.1.0", - "jsonc-parser": "~3.3.1", - "jsonpointer": "~5.0.1", - "markdownlint": "~0.37.4", - "minimatch": "~9.0.5", - "run-con": "~1.3.2", - "smol-toml": "~1.3.1" - }, - "bin": { - "markdownlint": "markdownlint.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/markdownlint-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/markdownlint-cli/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/markdownlint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/markdownlint/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/markdownlint/node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/markdownlint/node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/markdownlint/node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true - }, - "node_modules/markdownlint/node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast/-/mdast-3.0.0.tgz", - "integrity": "sha512-xySmf8g4fPKMeC07jXGz971EkLbWAJ83s4US2Tj9lEdnZ142UP5grN73H1Xd3HzrdbU5o9GYYP/y8F9ZSwLE9g==", - "deprecated": "`mdast` was renamed to `remark`", - "license": "MIT" - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-math": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", - "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "longest-streak": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.1.0", - "unist-util-remove-position": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", - "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", - "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "dev": true, - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "license": "MIT", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", - "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-math": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", - "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", - "dependencies": { - "@types/katex": "^0.16.0", - "devlop": "^1.0.0", - "katex": "^0.16.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", - "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mint": { - "version": "4.2.105", - "resolved": "https://registry.npmjs.org/mint/-/mint-4.2.105.tgz", - "integrity": "sha512-PXkw0OTktuQK6FyT52MBiIZispwN3DQKS6WBUWSnali9c/QBsagcHHX7he4mUpDyAlZP3GcIsNlIN1CSdWA5ZA==", - "license": "Elastic-2.0", - "dependencies": { - "@mintlify/cli": "4.0.709" - }, - "bin": { - "mint": "index.js", - "mintlify": "index.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-1.1.0.tgz", - "integrity": "sha512-xzB0UZFcW1UGS2xkXeDh39jzTP282lb3Vwp4QzCQYmkTn4ysaV5dBdbkOXmhkcE1TQlZebQlgTceaWvDr3oFgw==", - "deprecated": "This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.", - "dev": true, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "mkdirp": ">=0.5.0" - } - }, - "node_modules/mobx": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.0.tgz", - "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - } - }, - "node_modules/mobx-react": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.2.1.tgz", - "integrity": "sha512-WJNNm0FB2n0Z0u+jS1QHmmWyV8l2WiAj8V8I/96kbUEN2YbYCoKW+hbbqKKRUBqElu0llxM7nWKehvRIkhBVJw==", - "license": "MIT", - "dependencies": { - "mobx-react-lite": "^4.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - }, - "peerDependencies": { - "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/mobx-react-lite": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.1.1.tgz", - "integrity": "sha512-iUxiMpsvNraCKXU+yPotsOncNNmyeS2B5DKL+TL6Tar/xm+wwNJAubJmtRSeAoYawdZqwv8Z/+5nPRHeQxTiXg==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.4.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - }, - "peerDependencies": { - "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/nimma": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.3.tgz", - "integrity": "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==", - "license": "Apache-2.0", - "dependencies": { - "@jsep-plugin/regex": "^1.0.1", - "@jsep-plugin/ternary": "^1.0.2", - "astring": "^1.8.1", - "jsep": "^1.2.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - }, - "optionalDependencies": { - "jsonpath-plus": "^6.0.1 || ^10.1.0", - "lodash.topath": "^4.5.2" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", - "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch-h2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", - "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", - "dependencies": { - "http2-client": "^1.2.5" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/node-readfiles": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", - "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", - "dependencies": { - "es6-promise": "^3.2.1" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", - "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oas-kit-common": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", - "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", - "dependencies": { - "fast-safe-stringify": "^2.0.7" - } - }, - "node_modules/oas-linter": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", - "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", - "dependencies": { - "@exodus/schemasafe": "^1.0.0-rc.2", - "should": "^13.2.1", - "yaml": "^1.10.0" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-resolver": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", - "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", - "dependencies": { - "node-fetch-h2": "^2.3.0", - "oas-kit-common": "^1.0.8", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" - }, - "bin": { - "resolve": "resolve.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-resolver/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/oas-resolver/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/oas-resolver/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/oas-resolver/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/oas-schema-walker": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", - "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-validator": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", - "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", - "license": "BSD-3-Clause", - "dependencies": { - "call-me-maybe": "^1.0.1", - "oas-kit-common": "^1.0.8", - "oas-linter": "^3.2.2", - "oas-resolver": "^2.5.6", - "oas-schema-walker": "^1.1.5", - "reftools": "^1.1.9", - "should": "^13.2.1", - "yaml": "^1.10.0" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", - "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/open": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", - "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "wsl-utils": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi-sampler": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.6.1.tgz", - "integrity": "sha512-s1cIatOqrrhSj2tmJ4abFYZQK6l5v+V4toO5q1Pa0DyN8mtyqy2I+Qrj5W9vOELEtybIMQs/TBZGVO/DtTFK8w==", - "dependencies": { - "@types/json-schema": "^7.0.7", - "fast-xml-parser": "^4.5.0", - "json-pointer": "0.6.2" - } - }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "license": "MIT" - }, - "node_modules/os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "dependencies": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/outdent": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.8.0.tgz", - "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==", - "license": "MIT" - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-any": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-any/-/p-any-4.0.0.tgz", - "integrity": "sha512-S/B50s+pAVe0wmEZHmBs/9yJXeZ5KhHzOsgKzt0hRdgkoR3DxW9ts46fcsWi/r3VnzsnkKS7q4uimze+zjdryw==", - "license": "MIT", - "dependencies": { - "p-cancelable": "^3.0.0", - "p-some": "^6.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-some": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-some/-/p-some-6.0.0.tgz", - "integrity": "sha512-CJbQCKdfSX3fIh8/QKgS+9rjm7OBNUTmwWswAFQAhc8j1NR1dsEDETUEuVUtQHZpV+J03LqWBEwvu0g1Yn+TYg==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^4.0.0", - "p-cancelable": "^3.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", - "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", - "license": "MIT", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.6", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "license": "MIT", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-latin/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/patch-console": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", - "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "license": "MIT" - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "dev": true, - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT" - }, - "node_modules/perfect-scrollbar": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz", - "integrity": "sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/polished": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", - "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.17.8" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pony-cause": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz", - "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==", - "license": "0BSD", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/public-ip": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-5.0.0.tgz", - "integrity": "sha512-xaH3pZMni/R2BG7ZXXaWS9Wc9wFlhyDVJF47IJ+3ali0TGv+2PsckKxbmo+rnx3ZxiV2wblVhtdS3bohAP6GGw==", - "license": "MIT", - "dependencies": { - "dns-socket": "^4.2.2", - "got": "^12.0.0", - "is-ip": "^3.1.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/public-ip/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/public-ip/node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/puppeteer": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.15.0.tgz", - "integrity": "sha512-XjCY1SiSEi1T7iSYuxS82ft85kwDJUS7wj1Z0eGVXKdtr5g4xnVcbjwxhq5xBnpK/E7x1VZZoJDxpjAOasHT4Q==", - "deprecated": "< 24.10.2 is no longer supported", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.3.0", - "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1312386", - "puppeteer-core": "22.15.0" - }, - "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", - "integrity": "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==", - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "2.3.0", - "chromium-bidi": "0.6.3", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1312386", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.1" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-reconciler": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.32.0.tgz", - "integrity": "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-tabs": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.1.0.tgz", - "integrity": "sha512-6QtbTRDKM+jA/MZTTefvigNxo0zz+gnBTVFw2CFVvq+f2BuH0nF0vDLNClL045nuTAdOoK/IL1vTP0ZLX0DAyQ==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "prop-types": "^15.5.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/redoc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.0.tgz", - "integrity": "sha512-NpYsOZ1PD9qFdjbLVBZJWptqE+4Y6TkUuvEOqPUmoH7AKOmPcE+hYjotLxQNTqVoWL4z0T2uxILmcc8JGDci+Q==", - "license": "MIT", - "dependencies": { - "@redocly/openapi-core": "^1.4.0", - "classnames": "^2.3.2", - "decko": "^1.2.0", - "dompurify": "^3.2.4", - "eventemitter3": "^5.0.1", - "json-pointer": "^0.6.2", - "lunr": "^2.3.9", - "mark.js": "^8.11.1", - "marked": "^4.3.0", - "mobx-react": "^9.1.1", - "openapi-sampler": "^1.5.0", - "path-browserify": "^1.0.1", - "perfect-scrollbar": "^1.5.5", - "polished": "^4.2.2", - "prismjs": "^1.29.0", - "prop-types": "^15.8.1", - "react-tabs": "^6.0.2", - "slugify": "~1.4.7", - "stickyfill": "^1.1.1", - "swagger2openapi": "^7.0.8", - "url-template": "^2.0.8" - }, - "engines": { - "node": ">=6.9", - "npm": ">=3.0.0" - }, - "peerDependencies": { - "core-js": "^3.1.4", - "mobx": "^6.0.4", - "react": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/reftools": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", - "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rehype-katex": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", - "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/katex": "^0.16.0", - "hast-util-from-html-isomorphic": "^2.0.0", - "hast-util-to-text": "^4.0.0", - "katex": "^0.16.0", - "unist-util-visit-parents": "^6.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-minify-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.2.tgz", - "integrity": "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-minify-whitespace": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", - "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", - "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-math": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", - "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-math": "^3.0.0", - "micromark-extension-math": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx-remove-esm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remark-mdx-remove-esm/-/remark-mdx-remove-esm-1.2.0.tgz", - "integrity": "sha512-BOZDeA9EuHDxQsvX7y4ovdlP8dk2/ToDGjOTrT5gs57OqTZuH4J1Tn8XjUFa221xvfXxiKaWrKT04waQ+tYydg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.4", - "mdast-util-mdxjs-esm": "^2.0.1", - "unist-util-remove": "^4.0.0" - }, - "peerDependencies": { - "unified": "^11" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", - "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", - "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-applescript": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", - "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-async": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", - "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-con": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", - "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~4.1.0", - "minimist": "^1.2.8", - "strip-json-comments": "~3.1.1" - }, - "bin": { - "run-con": "cli.js" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==", - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-error": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-12.0.0.tgz", - "integrity": "sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==", - "license": "MIT", - "dependencies": { - "type-fest": "^4.31.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" - }, - "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shiki": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.12.2.tgz", - "integrity": "sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.12.2", - "@shikijs/engine-javascript": "3.12.2", - "@shikijs/engine-oniguruma": "3.12.2", - "@shikijs/langs": "3.12.2", - "@shikijs/themes": "3.12.2", - "@shikijs/types": "3.12.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==" - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-eval": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-eval/-/simple-eval-1.0.1.tgz", - "integrity": "sha512-LH7FpTAkeD+y5xQC4fzS+tFtaNlvt3Ib1zKzvhjv/Y+cioV4zIuw4IZr2yhRLu67CWL7FR9/6KXKnjRoZTvGGQ==", - "license": "MIT", - "dependencies": { - "jsep": "^1.3.6" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/simple-websocket": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/simple-websocket/-/simple-websocket-9.1.0.tgz", - "integrity": "sha512-8MJPnjRN6A8UCp1I+H/dSFyjwJhp6wta4hsVRhjf8w9qBHRzxYt14RaOcjvQnhD1N4yKOddEjflwMnQM4VtXjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "debug": "^4.3.1", - "queue-microtask": "^1.2.2", - "randombytes": "^2.1.0", - "readable-stream": "^3.6.0", - "ws": "^7.4.2" - } - }, - "node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/slugify": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.7.tgz", - "integrity": "sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/smol-toml": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz", - "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==", - "dev": true, - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stickyfill": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", - "integrity": "sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==" - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1" - } - }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ] - }, - "node_modules/style-to-js": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", - "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.9" - } - }, - "node_modules/style-to-object": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", - "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.4" - } - }, - "node_modules/styled-components": { - "version": "6.1.19", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz", - "integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==", - "license": "MIT", - "dependencies": { - "@emotion/is-prop-valid": "1.2.2", - "@emotion/unitless": "0.8.1", - "@types/stylis": "4.2.5", - "css-to-react-native": "3.2.0", - "csstype": "3.1.3", - "postcss": "8.4.49", - "shallowequal": "1.1.0", - "stylis": "4.3.2", - "tslib": "2.6.2" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/styled-components" - }, - "peerDependencies": { - "react": ">= 16.8.0", - "react-dom": ">= 16.8.0" - } - }, - "node_modules/styled-components/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "license": "0BSD" - }, - "node_modules/stylis": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", - "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", - "license": "MIT" - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swagger2openapi": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", - "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", - "license": "BSD-3-Clause", - "dependencies": { - "call-me-maybe": "^1.0.1", - "node-fetch": "^2.6.1", - "node-fetch-h2": "^2.3.0", - "node-readfiles": "^0.2.0", - "oas-kit-common": "^1.0.8", - "oas-resolver": "^2.5.6", - "oas-schema-walker": "^1.1.5", - "oas-validator": "^5.0.8", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" - }, - "bin": { - "boast": "boast.js", - "oas-validate": "oas-validate.js", - "swagger2openapi": "swagger2openapi.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/swagger2openapi/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/swagger2openapi/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/swagger2openapi/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/swagger2openapi/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", - "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trim-trailing-lines": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-2.1.0.tgz", - "integrity": "sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "license": "MIT", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unified/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-builder": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-4.0.0.tgz", - "integrity": "sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-builder/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-after/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-map/-/unist-util-map-4.0.0.tgz", - "integrity": "sha512-HJs1tpkSmRJUzj6fskQrS5oYhBYlmtcvy4SepdDEEsL04FjBrgF0Mgggvxc1/qGBGgW7hRh9+UBK1aqTEnBpIA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-map/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-position/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-remove": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", - "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-remove/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/unist-util-visit/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js-replace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", - "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", - "license": "MIT" - }, - "node_modules/urijs": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", - "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" - }, - "node_modules/url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", - "license": "BSD" - }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "license": "MIT" - }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/vfile-matter": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-5.0.1.tgz", - "integrity": "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==", - "license": "MIT", - "dependencies": { - "vfile": "^6.0.0", - "yaml": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-matter/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/vfile/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/widdershins": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widdershins/-/widdershins-4.0.1.tgz", - "integrity": "sha512-y7TGynno+J/EqRPtUrpEuEjJUc1N2ajfP7R4sHU7Qg8I/VFHGavBxL7ZTeOAVmd1fhmY2wJIbpX2LMDWf37vVA==", - "dev": true, - "dependencies": { - "dot": "^1.1.3", - "fast-safe-stringify": "^2.0.7", - "highlightjs": "^9.12.0", - "httpsnippet": "^1.19.0", - "jgexml": "latest", - "markdown-it": "^10.0.0", - "markdown-it-emoji": "^1.4.0", - "node-fetch": "^2.0.0", - "oas-resolver": "^2.3.1", - "oas-schema-walker": "^1.1.3", - "openapi-sampler": "^1.0.0-beta.15", - "reftools": "^1.1.0", - "swagger2openapi": "^6.0.1", - "urijs": "^1.19.0", - "yaml": "^1.8.3", - "yargs": "^12.0.5" - }, - "bin": { - "testRunner": "testRunner.js", - "widdershins": "widdershins.js" - } - }, - "node_modules/widdershins/node_modules/ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", - "dev": true, - "dependencies": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "node_modules/widdershins/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/widdershins/node_modules/better-ajv-errors": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz", - "integrity": "sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/runtime": "^7.0.0", - "chalk": "^2.4.1", - "core-js": "^3.2.1", - "json-to-ast": "^2.0.3", - "jsonpointer": "^4.0.1", - "leven": "^3.1.0" - }, - "peerDependencies": { - "ajv": "4.11.8 - 6" - } - }, - "node_modules/widdershins/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/widdershins/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/widdershins/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/widdershins/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/widdershins/node_modules/fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", - "dev": true - }, - "node_modules/widdershins/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widdershins/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/widdershins/node_modules/json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", - "dev": true - }, - "node_modules/widdershins/node_modules/jsonpointer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", - "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/widdershins/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widdershins/node_modules/oas-validator": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-4.0.8.tgz", - "integrity": "sha512-bIt8erTyclF7bkaySTtQ9sppqyVc+mAlPi7vPzCLVHJsL9nrivQjc/jHLX/o+eGbxHd6a6YBwuY/Vxa6wGsiuw==", - "dev": true, - "dependencies": { - "ajv": "^5.5.2", - "better-ajv-errors": "^0.6.7", - "call-me-maybe": "^1.0.1", - "oas-kit-common": "^1.0.8", - "oas-linter": "^3.1.3", - "oas-resolver": "^2.4.3", - "oas-schema-walker": "^1.1.5", - "reftools": "^1.1.5", - "should": "^13.2.1", - "yaml": "^1.8.3" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/widdershins/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widdershins/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/widdershins/node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/widdershins/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/widdershins/node_modules/swagger2openapi": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-6.2.3.tgz", - "integrity": "sha512-cUUktzLpK69UwpMbcTzjMw2ns9RZChfxh56AHv6+hTx3StPOX2foZjPgds3HlJcINbxosYYBn/D3cG8nwcCWwQ==", - "dev": true, - "dependencies": { - "better-ajv-errors": "^0.6.1", - "call-me-maybe": "^1.0.1", - "node-fetch-h2": "^2.3.0", - "node-readfiles": "^0.2.0", - "oas-kit-common": "^1.0.8", - "oas-resolver": "^2.4.3", - "oas-schema-walker": "^1.1.5", - "oas-validator": "^4.0.8", - "reftools": "^1.1.5", - "yaml": "^1.8.3", - "yargs": "^15.3.1" - }, - "bin": { - "boast": "boast.js", - "oas-validate": "oas-validate.js", - "swagger2openapi": "swagger2openapi.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/widdershins/node_modules/swagger2openapi/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widdershins/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widdershins/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/widdershins/node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/widdershins/node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/widdershins/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/widest-line/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", - "license": "MIT" - }, - "node_modules/widest-line/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/wsl-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", - "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", - "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xml2js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yaml-ast-parser": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", - "license": "Apache-2.0" - }, - "node_modules/yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "dependencies": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "node_modules/yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/yargs/node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yargs/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoga-layout": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", - "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", - "license": "MIT" - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index c62ee9cc..00000000 --- a/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "grid-api", - "version": "1.0.0", - "description": "Grid API Schema and Documentation", - "scripts": { - "build:openapi": "npx @redocly/cli bundle openapi/openapi.yaml -o openapi.yaml && cp openapi.yaml mintlify/openapi.yaml", - "prelint:openapi": "npm run build:openapi", - "lint:openapi": "npx @redocly/cli bundle openapi/openapi.yaml -o openapi.yaml && npx @redocly/cli lint openapi.yaml && cp openapi.yaml mintlify/openapi.yaml", - "lint": "npm run lint:openapi", - "broken-links": "cd mintlify && mint broken-links", - "validate": "npm run lint", - "test": "echo \"Error: no test specified\" && exit 1", - "cli:install": "cd cli && npm install", - "cli:build": "cd cli && npm run build", - "cli:dev": "cd cli && npm run dev" - }, - "dependencies": { - "@redocly/cli": "^1.34.5", - "mint": "^4.2.105" - }, - "devDependencies": { - "markdownlint-cli": "^0.44.0", - "widdershins": "^4.0.1" - }, - "author": "Lightspark Group", - "license": "Apache-2.0" -} diff --git a/scripts/build b/scripts/build new file mode 100755 index 00000000..16a2b00d --- /dev/null +++ b/scripts/build @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Building classes" +./gradlew build testClasses "$@" -x test diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes new file mode 100755 index 00000000..c59f25a6 --- /dev/null +++ b/scripts/detect-breaking-changes @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Detecting breaking changes" + +TEST_PATHS=( + grid-kotlin-core/src/test/kotlin/com/grid/api/models + grid-kotlin-core/src/test/kotlin/com/grid/api/services +) + +for PATHSPEC in "${TEST_PATHS[@]}"; do + # Try to check out previous versions of the test files + # with the current SDK. + git checkout "$1" -- "${PATHSPEC}" 2>/dev/null || true +done + +# Instead of running the tests, use the linter to check if an +# older test is no longer compatible with the latest SDK. +./scripts/lint diff --git a/scripts/format b/scripts/format new file mode 100755 index 00000000..9e294b97 --- /dev/null +++ b/scripts/format @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if command -v ktfmt &> /dev/null; then + echo "==> Running ktfmt" + find . -name "*.kt" -not -path "./buildSrc/build/*" -print0 | xargs -0 -r ktfmt --kotlinlang-style "$@" +else + echo "==> Running gradlew format" + ./gradlew format +fi diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 00000000..5837798c --- /dev/null +++ b/scripts/lint @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if command -v ktfmt &> /dev/null; then + echo "==> Checking ktfmt" + ./scripts/format --dry-run --set-exit-if-changed +else + echo "==> Running gradlew lint" + ./gradlew lint +fi diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 00000000..0b28f6ea --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 00000000..047bc1db --- /dev/null +++ b/scripts/test @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +echo "==> Running tests" +./gradlew test "$@" diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..c7d85c52 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,14 @@ +rootProject.name = "grid-kotlin-root" + +val projectNames = rootDir.listFiles() + ?.asSequence() + .orEmpty() + .filter { file -> + file.isDirectory && + file.name.startsWith("grid-kotlin") && + file.listFiles()?.asSequence().orEmpty().any { it.name == "build.gradle.kts" } + } + .map { it.name } + .toList() +println("projects: $projectNames") +projectNames.forEach { include(it) } From 0ca6e3566baccd2625c1157f02b459d71a80d8b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 19:22:39 +0000 Subject: [PATCH 2/3] chore: update SDK settings --- .github/workflows/detect-breaking-changes.yml | 2 +- .github/workflows/publish-sonatype.yml | 41 ++++++++++++ .github/workflows/release-doctor.yml | 24 +++++++ .release-please-manifest.json | 3 + .stats.yml | 2 +- README.md | 14 +++- bin/check-release-environment | 33 +++++++++ build.gradle.kts | 2 +- .../src/main/kotlin/grid.publish.gradle.kts | 6 +- .../main/kotlin/com/grid/api/core/Check.kt | 2 +- release-please-config.json | 67 +++++++++++++++++++ 11 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/publish-sonatype.yml create mode 100644 .github/workflows/release-doctor.yml create mode 100644 .release-please-manifest.json create mode 100644 bin/check-release-environment create mode 100644 release-please-config.json diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index fae14ad8..dfdb3c25 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -9,7 +9,7 @@ jobs: detect_breaking_changes: runs-on: 'ubuntu-latest' name: detect-breaking-changes - if: github.repository == 'stainless-sdks/grid-kotlin' + if: github.repository == 'lightsparkdev/grid-api' steps: - name: Calculate fetch-depth run: | diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml new file mode 100644 index 00000000..9762138c --- /dev/null +++ b/.github/workflows/publish-sonatype.yml @@ -0,0 +1,41 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to Sonatype in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/lightsparkdev/grid-api/actions/workflows/publish-sonatype.yml +name: Publish Sonatype +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Publish to Sonatype + run: |- + export -- GPG_SIGNING_KEY_ID + printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD" + GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')" + ./gradlew publish --no-configuration-cache + env: + SONATYPE_USERNAME: ${{ secrets.GRID_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.GRID_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.GRID_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.GRID_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 00000000..71c3aae8 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,24 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'lightsparkdev/grid-api' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v6 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + SONATYPE_USERNAME: ${{ secrets.GRID_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.GRID_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.GRID_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.GRID_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..1332969b --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.1" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 8f102128..e50b8800 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 44 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lightspark%2Fgrid-e951665c552dd2e74f6c8e51407409a787d01c88e6d3bdaf42b776c4dda290c8.yml openapi_spec_hash: e790ee4f8480260dbc6043a92f652022 -config_hash: ae718d4f8ed39c8caa45d820073d0b47 +config_hash: 6b9c5c4810cb47d647cdabbb2e9745e1 diff --git a/README.md b/README.md index a7e65683..1af84d35 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,26 @@ # Grid Kotlin API Library + + [![Maven Central](https://img.shields.io/maven-central/v/com.grid.api/grid-kotlin)](https://central.sonatype.com/artifact/com.grid.api/grid-kotlin/0.0.1) [![javadoc](https://javadoc.io/badge2/com.grid.api/grid-kotlin/0.0.1/javadoc.svg)](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.0.1) + + The Grid Kotlin SDK provides convenient access to the [Grid REST API](grid.lightspark.com) from applications written in Kotlin. It is generated with [Stainless](https://www.stainless.com/). + + KDocs are available on [javadoc.io](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.0.1). + + ## Installation + + ### Gradle ```kotlin @@ -27,6 +37,8 @@ implementation("com.grid.api:grid-kotlin:0.0.1") ``` + + ## Requirements This library requires Java 8 or later. @@ -737,4 +749,4 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/grid-kotlin/issues) with questions, bugs, or suggestions. +We are keen for your feedback; please open an [issue](https://www.github.com/lightsparkdev/grid-api/issues) with questions, bugs, or suggestions. diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 00000000..3a6a7b4a --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${SONATYPE_USERNAME}" ]; then + errors+=("The SONATYPE_USERNAME secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${SONATYPE_PASSWORD}" ]; then + errors+=("The SONATYPE_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${GPG_SIGNING_KEY}" ]; then + errors+=("The GPG_SIGNING_KEY secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${GPG_SIGNING_PASSWORD}" ]; then + errors+=("The GPG_SIGNING_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/build.gradle.kts b/build.gradle.kts index 43a2719a..5831f10e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ repositories { allprojects { group = "com.grid.api" - version = "0.0.1" + version = "0.0.1" // x-release-please-version } subprojects { diff --git a/buildSrc/src/main/kotlin/grid.publish.gradle.kts b/buildSrc/src/main/kotlin/grid.publish.gradle.kts index de3ddad4..25e06305 100644 --- a/buildSrc/src/main/kotlin/grid.publish.gradle.kts +++ b/buildSrc/src/main/kotlin/grid.publish.gradle.kts @@ -27,9 +27,9 @@ configure { } scm { - connection.set("scm:git:git://github.com/stainless-sdks/grid-kotlin.git") - developerConnection.set("scm:git:git://github.com/stainless-sdks/grid-kotlin.git") - url.set("https://github.com/stainless-sdks/grid-kotlin") + connection.set("scm:git:git://github.com/lightsparkdev/grid-api.git") + developerConnection.set("scm:git:git://github.com/lightsparkdev/grid-api.git") + url.set("https://github.com/lightsparkdev/grid-api") } versionMapping { diff --git a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt index 131ab595..3848bd62 100644 --- a/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt +++ b/grid-kotlin-core/src/main/kotlin/com/grid/api/core/Check.kt @@ -67,7 +67,7 @@ This can happen if you are either: Double-check that you are depending on compatible Jackson versions. -See https://www.github.com/stainless-sdks/grid-kotlin#jackson for more information. +See https://www.github.com/lightsparkdev/grid-api#jackson for more information. """ .trimIndent() } diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..8f987198 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,67 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "simple", + "extra-files": [ + "README.md", + "build.gradle.kts" + ] +} \ No newline at end of file From 431db706ebaf6b501a79b4e21833591a3f87123e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 19:23:07 +0000 Subject: [PATCH 3/3] release: 0.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1332969b..3d2ac0bd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1" + ".": "0.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..2868e3d9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog + +## 0.1.0 (2026-02-07) + +Full Changelog: [v0.0.1...v0.1.0](https://github.com/lightsparkdev/grid-api/compare/v0.0.1...v0.1.0) + +### Features + +* add isPlatformAccount to PaymentInstructions and remove PaymentAccountOrWalletInfo refs ([#128](https://github.com/lightsparkdev/grid-api/issues/128)) ([f05d11f](https://github.com/lightsparkdev/grid-api/commit/f05d11f0723d0740d2f63b6e1081f9c8ace7db9f)) +* add Stainless SDK config and misc cleanups ([#132](https://github.com/lightsparkdev/grid-api/issues/132)) ([30fbd62](https://github.com/lightsparkdev/grid-api/commit/30fbd62214cba0ac4faac93ba3a04920526f1497)) +* adding API design guidelines ([#137](https://github.com/lightsparkdev/grid-api/issues/137)) ([defc948](https://github.com/lightsparkdev/grid-api/commit/defc94817444b3c52c6c9ad6dbc7389c29f96783)) +* adding idempotency to transfers ([#138](https://github.com/lightsparkdev/grid-api/issues/138)) ([e84817d](https://github.com/lightsparkdev/grid-api/commit/e84817d8346ef5f55ac0cb37e93d2b16e5ca3a2e)) +* convert single-value enums to const for base types ([#126](https://github.com/lightsparkdev/grid-api/issues/126)) ([fc8dd03](https://github.com/lightsparkdev/grid-api/commit/fc8dd03497dec0dde3685292e7580d7089b62052)) +* rename accounts to data in list external accounts response ([#127](https://github.com/lightsparkdev/grid-api/issues/127)) ([10fa3eb](https://github.com/lightsparkdev/grid-api/commit/10fa3eb4762d6962658ef9c61ea9be22964ca81a)) +* rename Error to GridError and add additionalProperties to error schemas ([#131](https://github.com/lightsparkdev/grid-api/issues/131)) ([c9ec7a2](https://github.com/lightsparkdev/grid-api/commit/c9ec7a2ba5caa5a83b3659b73cdb3561b05b1752)) + + +### Bug Fixes + +* adding type:string to const discriminators ([#139](https://github.com/lightsparkdev/grid-api/issues/139)) ([fe683e6](https://github.com/lightsparkdev/grid-api/commit/fe683e6f4f6e7990b2bacee9566a4f76e2660b17)) +* reverting back to using enums instead of const for openapi generator ([#140](https://github.com/lightsparkdev/grid-api/issues/140)) ([1df8211](https://github.com/lightsparkdev/grid-api/commit/1df82118b36bf3c189d03c9d41b574fb49cd7bc3)) +* separating create customer from customer response schemas ([#142](https://github.com/lightsparkdev/grid-api/issues/142)) ([dde72cc](https://github.com/lightsparkdev/grid-api/commit/dde72ccbb05208db717e20d48b56225070aba03b)) + + +### Chores + +* sync repo ([865f8bb](https://github.com/lightsparkdev/grid-api/commit/865f8bb5360ca0f140185eae5546ce413ceca6be)) +* update SDK settings ([0ca6e35](https://github.com/lightsparkdev/grid-api/commit/0ca6e3566baccd2625c1157f02b459d71a80d8b6)) diff --git a/README.md b/README.md index 1af84d35..75d2ceaa 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.grid.api/grid-kotlin)](https://central.sonatype.com/artifact/com.grid.api/grid-kotlin/0.0.1) -[![javadoc](https://javadoc.io/badge2/com.grid.api/grid-kotlin/0.0.1/javadoc.svg)](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.0.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.grid.api/grid-kotlin)](https://central.sonatype.com/artifact/com.grid.api/grid-kotlin/0.1.0) +[![javadoc](https://javadoc.io/badge2/com.grid.api/grid-kotlin/0.1.0/javadoc.svg)](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.1.0) @@ -13,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/). -KDocs are available on [javadoc.io](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.0.1). +KDocs are available on [javadoc.io](https://javadoc.io/doc/com.grid.api/grid-kotlin/0.1.0). @@ -24,7 +24,7 @@ KDocs are available on [javadoc.io](https://javadoc.io/doc/com.grid.api/grid-kot ### Gradle ```kotlin -implementation("com.grid.api:grid-kotlin:0.0.1") +implementation("com.grid.api:grid-kotlin:0.1.0") ``` ### Maven @@ -33,7 +33,7 @@ implementation("com.grid.api:grid-kotlin:0.0.1") com.grid.api grid-kotlin - 0.0.1 + 0.1.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 5831f10e..4777b3ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ repositories { allprojects { group = "com.grid.api" - version = "0.0.1" // x-release-please-version + version = "0.1.0" // x-release-please-version } subprojects {

`kVFVqx!E$v_0$Z8Sj{M{R-u>-9FqG@ukR^ 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 diff --git a/mintlify/fonts/suisse-intl/SuisseIntl-Medium.woff2 b/mintlify/fonts/suisse-intl/SuisseIntl-Medium.woff2 deleted file mode 100644 index 04a947045483f4701a96aa6643ad73411ad53b36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51256 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 GAY3QN!_iUz diff --git a/mintlify/fonts/suisse-intl/SuisseIntl-Regular.woff2 b/mintlify/fonts/suisse-intl/SuisseIntl-Regular.woff2 deleted file mode 100644 index 2d2990acd585ee8e57d193dc2db6c7cde2ef984f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51596 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