diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 5de4be9..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cSpell.words": [ - "ruleset", - "Templating" - ] -} \ No newline at end of file diff --git a/examples/1.0.0/bnpl-arazzo.yaml b/examples/1.0.0/bnpl-arazzo.yaml new file mode 100644 index 0000000..adc833c --- /dev/null +++ b/examples/1.0.0/bnpl-arazzo.yaml @@ -0,0 +1,256 @@ +arazzo: 1.0.0 +info: + title: BNPL Workflow description + version: 1.0.0 +sourceDescriptions: + - name: BnplApi + url: https://raw.githubusercontent.com/OAI/Arazzo-Specification/main/examples/1.0.0/bnpl-openapi.yaml + type: openapi +workflows: +- workflowId: ApplyForLoanAtCheckout + summary: Apply for a loan at checkout using a BNPL platform + description: Describes the steps to secure a loan at checkout from a BNPL platform. It is a multistep process that requires multiple API calls across several API providers to be completed successfully. + inputs: + type: object + required: + - customer + - products + properties: + customer: + description: | + Customer can either be the customer details, which will be used for enrollment, or a link to an existing customer resource as the customer already uses the BNPL platform + oneOf: + - type: object + required: + - firstName + - lastName + - dateOfBirth + - postalCode + properties: + firstName: + description: First name of customer + type: string + minLength: 1 + maxLength: 70 + lastName: + description: Last name of customer + type: string + minLength: 1 + maxLength: 70 + dateOfBirth: + description: Customer date of birth + type: string + format: date-time + postalCode: + description: Zip code or postal code of customer + type: string + minLength: 1 + maxLength: 70 + additionalProperties: false + - type: object + required: + - uri + properties: + uri: + description: URI that points to an existing customer resource, as customer already enrolled on platform + type: string + format: uri + additionalProperties: false + products: + type: array + minItems: 1 + items: + type: object + required: + - productCode + - purchaseAmount + properties: + merchantCategoryCode: + description: Merchant category code of merchant. Only required for marketplace ecommerce platforms + type: string + pattern: '^[0-9]{4}$' + productCode: + description: Product code for loan application. Required for eligibility check + type: string + purchaseAmount: + description: Product purchase amount and currency code + type: object + required: + - currency + - amount + properties: + currency: + description: Currency code + type: string + pattern: "^[A-Z]{3}$" + amount: + description: Amount + type: number + steps: + - stepId: checkLoanCanBeProvided + description: | + Call the BNPL API to filter the basket for products qualifying for checkout loans. Pass in the array of products from the workflow input as the payload for the API call. + + Act on the response payload: + + - If a list of qualifying products is returned then submit customer choices. + - If the list of qualifying products is empty then end the workflow + operationId: findEligibleProducts + requestBody: + contentType: application/json + payload: | + { + "customer": "{$inputs.customer}", + "products": "{$inputs.products}" + } + successCriteria: + - condition: $statusCode == 200 + onSuccess: + - name: existingCustomerNotEligible + type: end + criteria: + - condition: $statusCode == 200 + - condition: $response.body.existingCustomerNotEligible == false + - name: qualifyingProductsFound + type: goto + stepId: getCustomerTermsAndConditions + criteria: + - condition: $statusCode == 200 + - context: $response.body + type: jsonpath + condition: $[?count(@.products) > 0] + - name: qualifyingProductsNotFound + type: end + criteria: + - condition: $statusCode == 200 + - context: $response.body + type: jsonpath + condition: $[?count(@.products) == 0] + outputs: + eligibilityCheckRequired: $response.body.eligibilityCheckRequired + eligibleProducts: $response.body.products + totalLoanAmount: $response.body.totalAmount + - stepId: getCustomerTermsAndConditions + description: | + Get the terms and conditions for the BNPL loans. This is static data and therefore has no arguments. + + The data will be displayed to the customer and they'll accept the terms out-of-band. + + After this step the flow will need to do a customer eligibility check if required. + operationId: getTermsAndConditions + successCriteria: + - condition: $statusCode == 200 + onSuccess: + - name: eligibilityCheckRequired + type: goto + stepId: createCustomer + criteria: + - condition: $steps.checkLoanCanBeProvided.outputs.eligibilityCheckRequired == true + - name: eligibilityCheckNotRequired + type: goto + stepId: initiateBnplTransaction + criteria: + - condition: $steps.checkLoanCanBeProvided.outputs.eligibilityCheckRequired == false + outputs: + termsAndConditions: $response.body + - stepId: createCustomer + description: | + Call the BNPL platform and verify the customer is eligible for the loan, which creates a customer resource. This step is skipped if the customer is already enrolled in the BNPL platform. + + Accepting the terms and conditions is set to true as the assumption is they have been accepted when this step is invoked. + + If the customer is eligible for the BNPL loan then a customer resource is created + operationId: createCustomer + requestBody: + contentType: application/json + payload: | + { + "firstName": "{$inputs.customer.firstName}", + "lastName": "{$inputs.customer.lastName}", + "dateOfBirth": "{$inputs.customer.dateOfBirth}", + "postalCode": "{$inputs.customer.postalCode}" + "termsAndConditionsAccepted": true + } + successCriteria: + - condition: $statusCode == 200 || $statusCode == 201 + onSuccess: + - name: customerIsEligible + type: goto + stepId: initiateBnplTransaction + criteria: + - condition: $statusCode == 201 + - name: customerIsNotEligible + type: end + criteria: + - condition: $statusCode == 200 + outputs: + customer: $response.body.links.self + - stepId: initiateBnplTransaction + description: Initiate the BNPL transaction by sending the customer identifier, eligible products, and indicative loan amount to initiate the loan process + operationId: createBnplTransaction + requestBody: + contentType: application/json + payload: | + { + "enrolledCustomer": "{$inputs.customer.uri}", + "newCustomer": "{$steps.createCustomer.outputs.customer}", + "products": "{$steps.checkLoanCanBeProvided.outputs.eligibleProducts}" + } + successCriteria: + - condition: $statusCode == 202 + onSuccess: + - name: CustomerAuthorizationRequired + type: goto + stepId: authenticateCustomerAndAuthorizeLoan + condition: $response.body.redirectAuthToken != null + - name: CustomerAuthorizationNotRequired + type: goto + stepId: retrieveFinalizedPaymentPlan + condition: $response.body.redirectAuthToken == null + outputs: + redirectAuthToken: $response.body.redirectAuthToken + loanTransactionResourceUrl: $response.body.links.self + - stepId: authenticateCustomerAndAuthorizeLoan + description: | + Authenticate the customer and seek authorization for the loan. + + Notes: + + - Authenticate in this case does not necessarily mean with a valid set of credentials. It could be to prove the identity of the end user, possibly doing an UMA style interaction (which is too complex to tie into this example). + - A redirect is returned for to be sent to the customer. The customer should follow this, but the success of the authorisation is out-of-band. + - This flow mimics OAuth style authorization but is not an exact match as it does not rely on an authorisation code/access token exchange (as this seems somewhat artificial in this context). + + operationId: getAuthorization + parameters: + - name: redirectAuthToken + in: query + value: $steps.authenticateCustomerAndAuthorizeLoan.outputs.redirectAuthToken + successCriteria: + - condition: $statusCode == 302 + outputs: + redirectUrl: $response.headers.Location + - stepId: retrieveFinalizedPaymentPlan + description: Retrieve finalized payment plan to show to customer once authorization is complete + operationId: retrieveBnplLoanTransaction + parameters: + - name: loanTransactionId + in: path + value: $steps.initiateBnplTransaction.outputs.loanTransactionId + successCriteria: + - condition: $statusCode == 200 + outputs: + finalizedPaymentPlan: $response.body + - stepId: updateOrderStatus + description: Send update from eCommerce platform to indicate order fulfilled and loan is therefore active + operationId: updateBnplLoanTransactionStatus + parameters: + - name: loanTransactionId + in: path + value: $steps.initiateBnplTransaction.outputs.loanTransactionId + requestBody: + contentType: application/json + payload: { "loanStatus": "Completed" } + successCriteria: + - condition: $statusCode == 204 + outputs: + finalizedPaymentPlan: $steps.retrieveFinalizedPaymentPlan.finalizedPaymentPlan \ No newline at end of file diff --git a/examples/1.0.0/bnpl-openapi.yaml b/examples/1.0.0/bnpl-openapi.yaml new file mode 100644 index 0000000..6a52a2a --- /dev/null +++ b/examples/1.0.0/bnpl-openapi.yaml @@ -0,0 +1,457 @@ +openapi: 3.0.0 +info: + title: Arazzo Buy-now, Pay-later Loan API + description: | + This OpenAPI description provides an example of a buy-now, pay-later (BNPL) API that contains multiple operations that allow eCommerce platforms to facilitate loans on behalf of customers + version: 1.0.0 +servers: + - url: /bnpl/v1 + description: Default BNPL instance +tags: + - name: Loan Initiation + description: Allows a loan to initiated and finalized +paths: + /auth: + get: + summary: Get customer authorisation + description: | + Used to initiate authentication of the End User and authorisation by the customer. + + Mimics an OAuth 2.0 style redirect, but cutdown for the purpose of an example. + operationId: getAuthorization + parameters: + - name: AuthorizationToken + in: query + description: Authorization token value elicited from loan initiation endpoint + required: true + schema: + $ref: '#/components/schemas/AuthorizationToken' + responses: + "302": + description: Instruction to redirect End User, based on validation of the authorisation token + headers: + Location: + description: URL to which the customer is redirected + schema: + type: string + default: + $ref: '#/components/responses/ErrorResponse' + /customers: + post: + summary: Create a customer + description: | + Create a customer for a BNPL loan if they are eligible for the loan in question. + + If a customer is eligible a customer resource is created, a 201 returned, and the a link to the customer resource returned. + + If the customer is not eligible a 200 is returned and a reason code indicating why the customer was rejected. + operationId: createCustomer + requestBody: + description: The customer properties + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/CustomerProperties' + - description: Terms and conditions have been reviewed and accepted + type: object + required: + - termsAndConditionsAccepted + properties: + termsAndConditionsAccepted: + type: boolean + responses: + "200": + description: Customer is not eligible for BNPL loan + content: + application/json: + schema: + type: object + required: + - reasonCode + properties: + reasonCode: + type: string + additionalProperties: false + "201": + description: Customer resource has been created and can be linked to loan transaction + content: + application/json: + schema: + type: object + required: + - customerId + - links + properties: + customerId: + description: Unique identifier for the newly created customer resource + type: string + links: + type: object + required: + - self + properties: + self: + description: URL identifying this resource + allOf: + - $ref: '#/components/schemas/CustomerUri' + additionalProperties: false + additionalProperties: false + default: + $ref: '#/components/responses/ErrorResponse' + /loan-transactions: + post: + summary: Initiate a new loan transaction + description: | + Initiate a new loan based on customer details and in-scope products. + + For the sake of this example: + + * There is one error response, defined using the `default` keyword. + operationId: createBnplTransaction + requestBody: + content: + application/json: + schema: + description: Properties for the loan. the `enrolledCustomer` and `newCustomer` properties are required to support the two different sources in the calling Arazzo description + type: object + required: + - products + - totalAmount + properties: + enrolledCustomer: + description: The customer resource URI for a previously enrolled customer + allOf: + - $ref: '#/components/schemas/CustomerUri' + newCustomer: + description: A newly-created customer resource URI for this loan + allOf: + - $ref: '#/components/schemas/CustomerUri' + products: + description: Product codes for products included in loan. Supplied to ensure any special terms are included in loan agreement + type: array + minItems: 1 + items: + $ref: '#/components/schemas/ProductCode' + totalAmount: + description: Loan amount being requested + allOf: + - $ref: '#/components/schemas/CurrencyAndAmount' + responses: + "202": + description: New loan initiated. This may require authorization before it is finalized + content: + application/json: + schema: + type: object + required: + - loanTransactionId + - links + properties: + redirectAuthToken: + description: A token that allows the loan to be completed without further authorisation. Is omitted if authentication and authorisation by the End User is required + loanTransactionId: + $ref: '#/components/schemas/LoanTransactionId' + links: + type: object + required: + - self + properties: + self: + description: Link to this resource + type: string + additionalProperties: false + additionalProperties: false + default: + $ref: '#/components/responses/ErrorResponse' + /loan-transactions/{loanTransactionId}: + parameters: + - $ref: '#/components/parameters/loanTransactionId' + get: + summary: Retrieve loan + description: Retrieve the finalised BNPL loan transaction with all installments + operationId: retrieveBnplLoanTransaction + responses: + "200": + description: Details of the loan transaction + content: + application/json: + schema: + $ref: '#/components/schemas/LoanTransaction' + default: + $ref: '#/components/responses/ErrorResponse' + /loan-transactions/{loanTransactionId}/status: + parameters: + - $ref: '#/components/parameters/loanTransactionId' + patch: + summary: Update loan status + description: Update the loan status to indicate order fulfilled and loan is active + operationId: updateBnplLoanTransactionStatus + requestBody: + content: + application/json: + schema: + type: object + required: + - status + properties: + status: + $ref: '#/components/schemas/LoanTransactionStatuses' + additionalProperties: false + responses: + "204": + description: Update to status acknowledged and applied to loan transaction + default: + $ref: '#/components/responses/ErrorResponse' + /products: + post: + summary: Retrieve eligible products + description: | + Retrieve the list of products that are eligible for a buy-now, pay-later loan. + + Implemented as a not particularly RESTful, RPC-style post operation for simplicity + operationId: findEligibleProducts + requestBody: + content: + application/json: + schema: + type: object + required: + - products + properties: + customer: + description: If the customer is already enrolled on the BNPL platform the customer URI can be supplied, which can be used to perform an upfront eligibility check + allOf: + - $ref: '#/components/schemas/CustomerUri' + products: + $ref: '#/components/schemas/Products' + additionalProperties: false + responses: + "200": + description: | + List of eligible products with information for subsequent steps + content: + application/json: + schema: + type: object + required: + - productCodes + properties: + existingCustomerNotEligible: + description: Flag to indicate existing customer found and is eligible. Associated workflows will stop if this flag is returned + type: boolean + productCodes: + description: This list of product codes that are eligible for a BNPL loan. Allows merchant to render screen showing matching products. Array will be empty if customer not eligible + type: array + items: + $ref: '#/components/schemas/ProductCode' + eligibilityCheckRequired: + description: Indicates whether the customer needs to be checked for eligibility. Required for new customers + type: boolean + default: false + totalAmount: + description: The total loan value for the products that are eligible + allOf: + - $ref: '#/components/schemas/CurrencyAndAmount' + additionalProperties: false + default: + $ref: '#/components/responses/ErrorResponse' + /terms-and-conditions: + get: + summary: Retrieve the terms and conditions for BNPL products + description: > + Retrieve the terms and conditions for BNPL products. + + For the sake of this example: + + * There is one set of customer T&Cs. + + * There is one error response, defined using the `default` keyword. + operationId: getTermsAndConditions + responses: + "200": + description: The terms and conditions document as an array of `string` values + content: + application/json: + schema: + type: array + items: + type: string + minItems: 1 + default: + $ref: '#/components/responses/ErrorResponse' +components: + parameters: + loanTransactionId: + name: loanTransactionId + description: Unique identifier for a given loan agreement + in: path + required: true + schema: + $ref: '#/components/schemas/LoanTransactionId' + responses: + ErrorResponse: + description: The error response details, encoded in the ProblemDetails format (RFC9457) + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' + schemas: + AuthorizationToken: + description: An authorisation token used to tee up a customer for authentication and authorisation of the loan as required + type: string + format: uuid + CustomerProperties: + type: object + required: + - firstName + - lastName + - dateOfBirth + - postalCode + properties: + firstName: + description: First name of customer + type: string + minLength: 1 + maxLength: 70 + lastName: + description: Last name of customer + type: string + minLength: 1 + maxLength: 70 + dateOfBirth: + description: Customer date of birth + type: string + format: date-time + postalCode: + description: Zip code or postal code of customer + type: string + minLength: 1 + maxLength: 70 + additionalProperties: false + CustomerUri: + description: The URI that identifies the customer resource for the loan transaction + type: string + format: uri + LoanTransaction: + description: Details of the loan transaction including product codes, total amount and repayment schedule + type: object + required: + - customer + - products + - totalAmount + - paymentSchedule + properties: + customer: + description: Link to customer resource + type: string + format: uri + products: + description: List of products and purchase amounts included in BNPL loan transaction + allOf: + - $ref: '#/components/schemas/Products' + status: + $ref: '#/components/schemas/LoanTransactionStatuses' + totalAmount: + description: The total loan amount including interest + allOf: + - $ref: '#/components/schemas/CurrencyAndAmount' + paymentSchedule: + description: Schedule of payments for loan repayment + type: array + minItems: 1 + items: + type: object + required: + - paymentDate + - amount + - lastPayment + properties: + paymentDate: + description: The date on which the payment is due + type: string + format: date + amount: + description: The currency and amount due + allOf: + - $ref: '#/components/schemas/CurrencyAndAmount' + lastPayment: + description: Indicator of whether this is the last payment that completes the loan repayment + type: boolean + default: false + additionalProperties: false + additionalProperties: false + LoanTransactionStatuses: + description: | + Loan transaction status values. Explanation + + * Pending: Loan transaction is pending authorisation by the End User + * Finalised: Loan transaction has been finalised and is awaiting completion + * Completed: Loan transaction has been issued following completion of order and payments will be collected + + type: string + enum: + - Pending + - Finalised + - Completed + LoanTransactionId: + description: Type for unique loan identifier + type: string + ProductCode: + description: Product code for loan application. Required for eligibility check + type: string + ProblemDetails: + type: object + required: + - type + properties: + type: + description: The problem type, expressed as a URI + type: string + status: + description: The HTTP return code generated by the server + type: string + pattern: "^[1-5][0-9]{2}$" + title: + description: The title of the problem, designed to be consumed by humans + type: string + detail: + description: A verbose error message, designed to be consumed by humans + type: string + instance: + description: A URI that identifies a specific occurrence of the problem + type: string + additionalProperties: true + CurrencyAndAmount: + description: Amount and currency code + type: object + required: + - currency + - amount + properties: + currency: + description: Currency code + type: string + pattern: "^[A-Z]{3}$" + amount: + description: Amount + type: number + Products: + type: array + minItems: 1 + items: + type: object + required: + - productCode + - netAmount + properties: + merchantCategoryCode: + description: Merchant category code of merchant. Only required for marketplace eCommerce platforms + type: string + pattern: '^[0-9]{4}$' + productCode: + $ref: '#/components/schemas/ProductCode' + purchaseAmount: + description: Product purchase amount and currency code + allOf: + - $ref: '#/components/schemas/CurrencyAndAmount'