3

For ex. I need to design an API with two features: payment validation and payment creation. I need both because the end user should know that everything is ok before actually confirm the payment creation. For creation I have

POST .../payment

with an input body. It returns HTTP 400 in case something went wrong.

The validation (or simulation) does the exact same process than the creation, stopping the process just before persisting the data. For this validation, is it better to have something like

Solution 1

GET .../is-payment-ok

also with an input body. It returns HTTP 200 including a boolean answer and some details.

Here the resource is not the payment, it's the information about the validity of the payment, which seems REST-compliant to me. The drawback being that the user of the API could be confused because if the payment is not valid, then the simulation would return HTTP 200 (with the boolean in the body set to false) while the creation would return HTTP 400.

, or

Solution 2

POST .../payment?simulation=true , or
POST .../payment-simulation

Here the HTTP response code would be exactly the same as for a payment creation. The drawback being that we use a POST, without actually 'posting' any resource.

How would you do it ? Is there a REST rule or common practice for this case ?

ThCollignon
  • 976
  • 3
  • 14
  • 31

5 Answers5

6

While not strictly forbidden, GET requests usually have no request bodies: HTTP GET with request body, so I would not go with Solution 1.

In our APIs (Payment APIs too) we use a query param with the same URL to flag a dry-run (or forecast in our domain terms), just like your Solution 2, and as described here: Dry run strategy for REST API. It gives back the same error codes and messages that non-forecast requests would yield in case of an error.

We needed this due to we use a reactive payment processor, but it only starts if the payment-forecast succeeded. The process give back this forecast result as a response to the user immediately after the process starts, increasing the responsivity of the UI. The actual payment will take a second to be sent for real processing, as some checks are done reactively with other services. It may even fail and the end-user will get a notification about the final status, but this will be very rare.

If you check the HTTP spec, how POST processes a resource is pretty arbitrary, there is no need to make a persistent change in anything: https://www.rfc-editor.org/rfc/rfc7231#section-4.3.3

The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics

[...]

If one or more resources has been created on the origin server as a result of successfully processing a POST request, the origin server SHOULD send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created (Section 7.1.2) and a representation that describes the status of the request while referring to the new resource(s).

Just make sure to document the query param. EDIT: Kubernetes does it: https://kubernetes.io/docs/reference/using-api/api-concepts/#dry-run

The Stripe API is a bit different since if you send a request it will be booked, there is no dry-run (the /capture call is called by the money requester, not the payer, so it is another use-case).

Community
  • 1
  • 1
David Szalai
  • 2,363
  • 28
  • 47
  • Is the GET the only reason why you discard Solution 1 ? If so I could also propose to use a POST, still with another endpoint than the one used for the persisting step. Would this solution be worse than using a flag, to your opinion ? – ThCollignon Nov 21 '19 at 10:19
  • Btw, thanks for the link about the [dry-run strategy](https://stackoverflow.com/questions/31967684/dry-run-strategy-for-rest-api), very useful. – ThCollignon Nov 21 '19 at 10:20
  • yeah discarded due to get, but it boils down to personal preferences if you want to use a different endpoint or stick with the flag concept. For me, the flag was easier to implement and document. Also, just found out that Kubernetes API does it too :) Link in answer. – David Szalai Nov 21 '19 at 12:51
0

I would suggest you to create 2 endpoints, this is commonly done among payment service providers like UsaEPay,Stripe,etc.

The first endpoint will be something like: POST .../authorize and will receive all the information needed to make the payment such as the amount, payer info and payment method info, then it will create a paymentAuthorization and return the status of it among with an authorizationToken if the authorization succeeded.

The second endpoint will be something like: POST .../capture, in this endpoint you will only look for the paymentAuthorization related to the authorizationToken you generated before and proceed to make the payment

Also you could take a look a the Stripes documentation https://stripe.com/docs/api/payment_intents to understand more about this kind of system structure

sebrojas
  • 881
  • 10
  • 15
  • Thank you for your answer, but we don't intend to persist anything in the _authorization_ call. Our simulation does the exact same process than the confirmation, except the persistance. – ThCollignon Nov 21 '19 at 10:11
0

I agree with the other answers for using your option 2 with two endpoints to reduce ambiguity.

You use the term validation in your question, and I believe it relays what is going on better than simulation. I would expect a simulation call return the same response as the payment, but not allow the payment to go through.

A validation request would more intuitively let the user know that the API would evaluate the request and at the very least tell you if it was acceptable or not.

Option 1 would go against standard design principles, as a GET request typically has the body ignored if it exists.

Follow your domain's uri path design, or use some of the resources mentioned in the other answers.

POST .../Payment
POST .../Validate/Payment
Noel
  • 600
  • 16
  • 37
0

Depending what type of payments you're working with, you may be interested in a two-phase commit approach. This also aligns more with the REST interface than the RPC-like "is this ok" call.
The idea here is that you create one resource (let's call it the payment authorization, POST /payment-authorizations) which ensures all resources are available, in this case putting a hold on the user's account for the amount of the payment, and returning the id of the new payment.

Now the user can review the payment (GET /payment-authorizations/{id}).
They can cancel it if they want (DELETE /payment-authorizations/{id}), releasing the funds.
Or they can finalize it (PUT /payments/{id}).

The main advantages of this approach are that

  • It's RESTfull
  • Payments are idempotent (you can try to finalize a payment as many times as you want, but it will only ever send the money once)
  • The payment validation step is ACTUALLY USEFUL

Elaborating more on that last item: these API calls you're making are asynchronous, and race conditions are fully possible. You could have a scenario like

Alice                        Alice's Account    Chuck
                                  $10
"can I send $5 to Bob?" ->        
                        <-       "yes"  
                                             <- "can I withdraw $6 for rent?"
                                 "yes"       ->
                                             <- "withdraw $6 for rent"
                                 "ok"        ->
                                  $4
"send $5 to Bob"        ->
                        <- "insufficient funds"

Now your client is confused, the system said it could make a payment, but it can't. With a two-phase commit you'd have

Alice                        Alice's Account    Chuck
                                  $10
"authorize $5 to Bob" ->      "ok, authz #5"
                                  $5(+5)
                                             <- "authorize $6 for rent"
                        "insufficient funds" ->
"send payment #5"     ->         "ok"
                                  $5

Now Alice is still broke, but all the responses we've given are accurate.

kag0
  • 5,624
  • 7
  • 34
  • 67
  • This is an interesting answer but functionnally it doesn't fit our needs. We don't want to persist authorizations, and race conditions as you described are acceptable. – ThCollignon Nov 22 '19 at 05:38
-1

As i can see, you are interested in URI Path Design.

Usually:

  • Singular Nouns are used as to refer a single document resourse into your URL.
  • Plural Nouns are used to refer to a collections or stores.
  • Verrbs represent actions, which can be considered as a controller, like Validate or Store.

In REST, a request of type POST can be used to create a new resource into a collection or execute a controller. So, using Post seems to be a correct choice for your API.

Now, in your case, you need to perform two actions Create and Validate:

In your case, i would create two possible URL, one for create a Payment and another one to validate it, sending an Id of the payment.

POST .../Create/Payment
POST .../Simulate/Payment/{IdPayment}

I considered Payment as a single document and Create and Simulate as a controller. In the case of Simulate, i adding am Id to the Payment document to identify it.

There is a book, from O'Reilly, which is easy to read and has all the information that you are looking for. This book is REST API Design Rulebook

I hope this information could be helpful for you.

Jorge Omar Medra
  • 978
  • 1
  • 9
  • 19