opasch / fidelity

Blockchain based fidelity program
GNU General Public License v3.0
4 stars 0 forks source link

response to the backend once a rule is satisfied #15

Open opasch opened 3 years ago

opasch commented 3 years ago

Once a rule inside the rule engine is satisfied (once a user as completed a task for example) the Rule Engine has to communicate with the backend to send the amount of token to be paid and the wallet id. The operation can be a REST API which transfers a json file. However it is useful to note that the operation has to be asynchronous, meaning that the backend doesn't necessarily always have to expect an immediate response once it sends the cart content because the rule engine that keep a rule running and not being satisfied for relatively long time. Once the rule engine is ready to send a payment information to the backend then it will do it.

opasch commented 3 years ago

@sblabreu I created this issue for @jambtc to create a backend response API to receive the payment information.

jambtc commented 3 years ago

to @opasch and @sblabreu I transcribe here what has already been said in our mail exchange

Hi, Sergio here are the informations I hope we need. I registered these keys in the archive of the backend server

Public Key: you can read this in email Private Key: you can read this in email

URI to send Rule Engine Response in POST form and with the signature in the header as explained in the api-key-rules.md file. https://github.com/opasch/fidelity/blob/shopping-cart/API-Keys-rules.md

URL: https://dashboard.fidelize.tk/index.php?r=ipn/rules

For future flexibility, we may consider including the URL information in the POST message.

Please now I need the URL where to send the backend request so I can try to do some tests.

For more information I took a cue from the Kraken api keys. On this site you can find the information you need to generate keys in different languages, php, java, go, python, etc.

jambtc commented 3 years ago

Thanks, I will give it a go. Do you have a payload example?

At the moment I do not have the rule engine deployed anywhere

Here is an example of an event POST

curl -X POST "http://localhost:4000/api/v1/event" -H "accept: application/json" -H "x-fre-origin: 1234" -H "authorization: Basic YWRtaW46YWRtaW4=" -H "content-type: application/json" -d "{ \"event\": { \"cart_id\": \"9roj8f70TUEU\", \"client_address\": \"0xab96032f5a7Efe3F95622c5B9D98D50F96a91756\", \"items\": [ { \"product_id\": \"1\", \"product_name\": \"product_1\", \"product_price\": 5 } ], \"merchant_id\": \"1234\", \"total_items\": 3, \"total_price\": 15 }}"

Response 201

{
  "event": {
    "group": {
      "total_price_eq": [
        "ok"
      ]
    }
  }
}

or 
Response 422

{
  "errors": {
    "detail": "Event not valid, check header combination for merchant_id Parameter"
  }
}

The openapi json spec in attached for remaining operations

swagger.json

jambtc commented 3 years ago

One action i plan to add is to pass on the received event payload .

This means,

If you send an event to transfer tokens from one account to another then if a rule match on a specific parameter key value then the action will be to post the received event to the backend ( no modifications)

jambtc commented 3 years ago

@sblabreu The Payload example is the following:

{ "id": "FT1zK0k4wo", "redirect_url": "http://localhost/fidelize-dashboard/index.php?r=ipn/rules", "merchant_id": "2", "customer_id": "2", "order_number": "94729", "order_total": "1249", "items": [ { "product_id": "2", "product_qty": "1", "product_price": "1200", "product_description": "iMac" }, { "product_id": "4", "product_qty": "1", "product_price": "49", "product_description": "iPod Shuffle" } ], "client_address": "0xda9e2a20a018717d073a7acdfe93a099453b6844", "cart_id": "FT1zK0k4wo", "nonce": "1606723672172196" }

jambtc commented 3 years ago

This is also for issues #13, #14

Now it is possible to use the shopping cart emulator to perform a payment simulation and obtain an almost similar to real payload. When you connect for the first time, you are prompted for API keys. If you need new API keys you can get them using a merchant account on the Fidelize dashboard. For testing, you may use these:

Public KEY: d8cQO5ZmT9J7SfgsoNM0ECVi Private KEY: ueaB0H}3P?h25gV2dvk\%NzUAvl1tVaOjpf7R56hq~mqykEYgfKD0K1iBs|3ZGc/

Use the shopping cart at this address: shopping.fidelize.tk

A PAY button simulates the payment of the order and sends the payload data to the backend.

jambtc commented 3 years ago

@sblabreu Did something hit your server? Can you confirm the URL I should send the post? I received some tls error will try to build the post request manually instead with the lib.

jambtc commented 3 years ago

Yes, I did. Like a fool, I reversed the server ip and deleted the blockchain on one side, and on the other I messed up the applications. Today I restored the POA Blockchain, and now I got to work on applications.

BTW, I think you mean the URL where the Rules Engine have to send its response, right? OK, the correct URL is : https://dashboard.fidelize.tk/index.php?r=ipn/rules

I provided to send it in the payload as "redirect_url" field, in this way we can manage differents url from different servers. The response, at this time, is in json format and return only the headers and the post you send. Randomly it generates a "fake error" returning success=false.

The last thing, I inserted 'x-fre-origin: 1234' in the headers, but I keep getting the answer "unauthorized". Still missing something else?

Bye.

jambtc commented 3 years ago

@sblabreu

Hi Sergio,

Just deploy version 0.0.2 what changes:

curl -X POST "http://164.68.126.56:5000/api/v1/event" -H "accept: application/json" -H "x-fre-origin: 1234" -H "Authorization: cZn7PNFOAkBE4H3Ift2h5rJo" -H "content-type: application/json" -d "{ \"event\": { \"cart_id\": \"9roj8f70TUEU\", \"client_address\": \"0xab96032f5a7Efe3F95622c5B9D98D50F96a91756\", \"items\": [ { \"product_id\": \"1\", \"product_name\": \"product_1\", \"product_price\": 5 } ], \"merchant_id\": \"1234\", \"total_items\": 3, \"total_price\": 15 }}" Added the API-Key as Authorization header. later I can create similar keys from the hash of the merchant schema

Now I still not able to send a POST to you. apart from the wiki , can you send a real scenario. I got confused with the "$postdata = http_build_query($request, '', '&');" where to use it when building the signature. currently also having some TLS issues still trying out forcing tls v1.2 Cheers, SA

jambtc commented 3 years ago

the postdata is formed in this way:

We start from an array

$data = array(
    'foo' => 'bar',
    'baz' => 'boom',
    'cow' => 'milk',
    'php' => 'hypertext'
);

we use the function echo http_build_query($data, '', '&');

And the RESPONSE is:

foo=bar&baz=boom&cow=milk&php=hypertext

in simple terms it is the query of an url

jambtc commented 3 years ago

I'm still receiving "Unauthorized" from rules engine server.

This is the payload (obviusly I transform it in json) New Payload to Rules Engine Server is:

stdClass Object
(
    [id] => fidelity:2KQed0pBlT
    [redirect_url] => http://localhost/fidelize-dashboard/index.php?r=ipn/rules
    [merchant_id] => 1234
    [customer_id] => 2
    [order_number] => 63701
    [order_total] => 99
    [items] => Array
        (
            [0] => Array
                (
                    [product_id] => 5
                    [product_qty] => 1
                    [product_price] => 99
                    [product_description] => iPod Nano
                )

        )

    [total_price] => 15
    [nonce] => 1608630256909366
    [client_address] => 0xda9e2a20a018717d073a7acdfe93a099453b6844
    [cart_id] => 2KQed0pBlT
)

and this is the header

New Header to Rules Engine Server is:

Array
(
    [0] => API-Key: cZn7PNFOAkBE4H3Ift2h5rJo
    [1] => API-Sign: 8sE+ct+CuMvNCcql/9eRQQkfwogmYqqUvqWa/OkaJ9EHdZfOF/o6pm3y+o6ZHbzDiMTF01RiNqSr01ZYFLiDtg==
    [2] => x-fre-origin: 1234
    [3] => Authorization: cZn7PNFOAkBE4H3Ift2h5rJo
    [4] => content-type: application/json
    [5] => accept: application/json
)

Have you some ideas?

jambtc commented 3 years ago

@sblabreu Fixed, was the docker environment variable ....

@sergio casizzone from your mail I noticed a few things.

1) you are sending the API-Key and API-Sign as headers ..... I will check to use those for the POST and not environment settings.

2) such payload schema is not defined for the merchant 1234 i created a new merchant id 4321 with match payload schema.

Please also send me a real example to build the POST to backend ..... sorry for that but I a bit lost .... Cheers,

jambtc commented 3 years ago

Hi Sergio,

Thanks

What shall I use on the array?

What I was planning to do is:

POST body copy of your event POST body

{
    "id": "fidelity:2KQed0pBlT",
    "redirect_url":  "http://localhost/fidelize-dashboard/index.php?r=ipn/rules",
    "merchant_id":  1234,
    "customer_id":  2,
    "order_number":  63701,
    "order_total":  99,
    "items":  [
        {
            "0":  [
                {
                    "product_id":  5,
                    "product_qty":  1,
                    "product_price":  99,
                    "product_description":  "iPod Nano"
                }
]
}
        ],

    "total_price":  15,
    "nonce":  1608630256909366,
    "client_address":  "0xda9e2a20a018717d073a7acdfe93a099453b6844",
    "cart_id":  "2KQed0pBlT"
}

What should be the parameters to build the signature?

Cheers, SA

jambtc commented 3 years ago

@sblabreu

these are the parameters you must send in the header:

'API-Key:  publickey',
'API-Sign: sign',
'Content-Type: application/json',
'Accept: application/json',

and the payload you must send to backend is something like this:

{
    "id": "fidelity:2KQed0pBlT",    // the id
    "merchant_id":  1234,            // the merchant id rules engine has checked
    "customer_id":  2,                  // the customer id  "        "            "       "
    "client_address":  "0xda9e2a20a018717d073a7acdfe93a099453b6844",
    "cart_id":  "2KQed0pBlT",     // the cart id
    "actions":  [                            // the actions to do in an array, in this way we can manage more actions 
        {
            "0":  [
                {
                    "send_mail":  true,  // what actions we can do? 
                    "token_qty":  5,
                }
            ]
        }
   ],
   "nonce":  1608630256909366,

Then at this point, we must define what action the backend have to do. Maybe we can do some kind of coding of actions. For exmple:

{
    '0100' : 'send mail',
    '0200' : 'send token',
    '0300' : 'send push message',
    '0400' : 'some other action',
}

I ask to @opasch if we have to open a new issue for that or not.

sblabreu commented 3 years ago

Hi,

For the api-sign I read the wiki but can you provide the steps using the example above.

As for the actions it's an list so I can pattern matching on a tuple like: ["0100","0200"] or [{"0100", "xxxxx"},{"0200","yyyy"}]

SA

jambtc commented 3 years ago

I know, you mean how to generate the sign with api keys.

First of all, I gave you the public and private key. Well, in the API-Keys-rules.md I said: API-Sign = A base64 encoded message signature using HMAC-SHA512 of (SHA256(nonce + POST data)) and base64 decoded of md5 hash of (public API key + secret API key) What does this mean?

We have to manage these variables: nonce, postdata, public and secret.

  1. nonce: it's the current timestamp at microsecond resolution
  2. postdata: it's the payload POST that have to be transformed in something else like this example:
    
    // this is the POST array
    array(
    "id" => "1234567",
    "merchant_id" => "4321",
    "client_address" =>  "0xda9e2a20a018717d073a7acdfe93a099453b6844",
    "nonce" => "1608630256909366",
    )

// this is the transformed payload to be used in the sign id=1234567&merchant_id=4321&client_address=0xda9e2a20a018717d073a7acdfe93a099453b6844&nonce=1608630256909366


3. public: it's the public key 
4. secret: it's the md5 hash of the concatenation of the public key plus the private key. Example: md5(public + private)

At this point we have to use some functions (I copied the explanations from [php.net](php.net) site):

1. base64_encode — Encodes data with MIME base64
2. base64_decode — Decodes data encoded with MIME base64
3. md5 — Calculate the md5 hash of a string
4. **hash** — Generate a hash value (message digest)
5. **hash_hmac** — Generate a keyed hash value using the HMAC method

Start using the **md5** function:
```php
// set the secret 
// description: md5 ( string $string [, bool $binary = false ] ) : string
secret = md5(public + private); 

In php is used the . (point) to concatenate two or more strings. Maybe in some other language (like javascript) you have to use the plus (+) character.

Now we use the hash function with sha256 algorithm:

// set the sha256 content using the **hash** function
// description: hash ( string $algo , string $data [, bool $binary = false ] ) : string|false
sha256_content = hash('sha256', nonce + postdata, true)
// When set $binary to true, it outputs raw binary data

nonce + postdata is a string formed by the concatenation of the nonce and postdata variables.

Then, we have to use the hash_hmac function with the sha512 algorithm:

// set the sha512 content using the **hash_hmac** function
// description: hash_hmac ( string $algo , string $data , string $key [, bool $binary = false ] ) : string|false
sign = hash_hmac('sha512', sha256_content, base64_decode( secret ), true)
// When $binary is set to true, it outputs raw binary data. false outputs lowercase hexits.

In the above function we used the sha256_content and the base64_decode function to decode the secret.

Finally, we are ready to generate the encoded sign

// set the sign to send in the header of the web request
sign_encoded = base64_encode( sign )

We have to use the base64_encode function to encode the sign in the header.

That's all folks. It has been long and I hope I have been clearer this time.

P.S. At the time of writing, none of the above is still active. However, just make a whistle and I activate the control on the keys. I have left the system open for now so that you can try anything you want and read for yourself the response you can send to the backend.

jambtc commented 3 years ago

Hi @sblabreu I'm getting this response from server : "No Valid Schema Detected or payload not according to defined schema"

The payload I'm sending is this:

tdClass Object
(
    [event] => stdClass Object
        (
            [merchant_id] => 1234
            [items] => Array
                (
                    [0] => Array
                        (
                            [product_id] => 4
                            [product_name] => iPod Shuffle
                            [product_price] => 49
                        )

                )

            [total_items] => 1
            [total_price] => 15
            [client_address] => 0xda9e2a20a018717d073a7acdfe93a099453b6844
            [cart_id] => uj9TQ5GBMS
        )

    [nonce] => 1608801504879230
)

// this is in json format
{"event":{"merchant_id":"1235","items":[{"product_id":"4","product_name":"iPod Shuffle","product_price":"49"}],"total_items":"1","total_price":"15","client_address":"0xda9e2a20a018717d073a7acdfe93a099453b6844","cart_id":"S32KDbRuZU"},"nonce":"1608802432823684"}

Do you find something wrong? Maybe I cannot send the nonce?

sblabreu commented 3 years ago

Use the nonce in same level as merchant_id and not on the level of the event.

We use event to pattern matching inside the controller your post like that would drop the nonce.

jambtc commented 3 years ago

Right. Now I have another problem. When I send the payload the system transform the array in a json encoded string. All string digits are transformed in numbers and the merchant_id too. Rule engine response is "Event not valid, check header combination for merchant_id Parameter" because it wants a string. If I don't transform with the JSON_NUMERIC_CHECK parameter in the json_encode function, the response is "No Valid Schema Detected or payload not according to defined schema" because some numbers are formatted like string. I think the best way to solve is your schema could accept merchant_id like a number. Do you agree?

jambtc commented 3 years ago

This is the starting PHP object

stdClass Object
(
    [event] => stdClass Object
        (
            [id] => fidelity:NIHlnz5Ftb
            [redirect_url] => http://localhost/fidelize-dashboard/index.php?r=ipn/rules
            [merchant_id] => 1234
            [customer_id] => 2
            [order_number] => 51612
            [order_total] => 99
            [items] => Array
                (
                    [0] => Array
                        (
                            [product_id] => 5
                            [product_name] => iPod Nano
                            [product_price] => 99
                        )

                )

            [total_items] => 1
            [total_price] => 15
            [nonce] => 1608810363358342
            [client_address] => 0xda9e2a20a018717d073a7acdfe93a099453b6844
            [cart_id] => NIHlnz5Ftb
        )

)

This is the string obtained w/ json_encode function with JSON_NUMERIC_CHECK parameter and I get "Event not valid, check header combination for merchant_id Parameter" from server

{"event":{"id":"fidelity:NIHlnz5Ftb","redirect_url":"http:\/\/localhost\/fidelize-dashboard\/index.php?r=ipn\/rules","merchant_id":1234,"customer_id":2,"order_number":51612,"order_total":99,"items":[{"product_id":5,"product_name":"iPod Nano","product_price":99}],"total_items":1,"total_price":15,"nonce":1608810363358342,"client_address":"0xda9e2a20a018717d073a7acdfe93a099453b6844","cart_id":"NIHlnz5Ftb"}}

This is the string obtained w/ json_encode function w/o parameters and I get "No Valid Schema Detected or payload not according to defined schema" from server

{"event":{"id":"fidelity:NIHlnz5Ftb","redirect_url":"http:\/\/localhost\/fidelize-dashboard\/index.php?r=ipn\/rules","merchant_id":"1234","customer_id":"2","order_number":"51612","order_total":"99","items":[{"product_id":"5","product_name":"iPod Nano","product_price":"99"}],"total_items":"1","total_price":"15","nonce":"1608810363358342","client_address":"0xda9e2a20a018717d073a7acdfe93a099453b6844","cart_id":"NIHlnz5Ftb"}}
sblabreu commented 3 years ago

Merchant_id can also be an address like client_id that's the reason I did not make it a number.

jambtc commented 3 years ago

I save this link as as reminder. It may be the solution I'm searching...

https://stackoverflow.com/questions/24520950/json-numeric-check-and-phone-numbers

jambtc commented 3 years ago

I noticed that if I use a merchant id different from 1234 the server responds "No Valid Schema Detected or payload not according to defined schema". Maybe could be better "No merchant id found" or similar. That answer is ambiguous.

Anyway, I haven't found a way to make the merchant id work as a string and the rest as numbers. Can we try to use merchant id as hexadecimal number? You said it's also used as an address. Well, an address start with '0x' like a hexadecimal number. What do you think about this solution?

sblabreu commented 3 years ago

Open a ticket for the first paragraph #16 As for the merchant_id, issue #17 is open, payload should only follow the draft json validation

Regarding the actual request towards the backend, I'm receiving certificate issue

"(...) [info] TLS :client: In state :wait_cert_cr at ssl_handshake.erl:1950 generated CLIENT ALERT: Fatal - Unknown CA

{:error, {:tls_alert, {:unknown_ca, 'TLS client: In state wait_cert_cr at ssl_handshake.erl:1950 generated CLIENT ALERT: Fatal - Unknown CA\n'}}} (..)"

Below is the same request towards the webhook.site as testing ..... also uses SSL but is success.

https://webhook.site/#!/f0eb8112-c277-443b-a7fa-dd966e718bc0/c5b865b2-f724-4547-a9d6-e5a10c53d0ec/1

sblabreu commented 3 years ago

Added rule action "proxy_backend" which will proxy the event payload case a rule matches.

Might that something hit the backend, @jambtc can you please check it?

23:20:56.991 request_id=FlkCSZ0x8iJqTvkAAAIB [info] Sent 200 in 36ms Starting work... process: : :ok ...Finished work 23:22:15.918 request_id=FlkCWM0Tl_DUkAAAJB [info] POST /api/v1/event 23:22:15.945 request_id=FlkCWM0Tl_DUkAAAJB [info] Sent 200 in 26ms Starting work... process: : :ok ...Finished work 23:22:47.456 request_id=FlkCY1eaplW1WYkAAAKB [info] POST /api/v1/event 23:22:47.474 request_id=FlkCY1eaplW1WYkAAAKB [info] Sent 200 in 18ms Starting work... process: : :ok ...Finished work

jambtc commented 3 years ago

Ok. We are now ready to prepare the backend to receive a POST request from the Rules Engine.

I know that, for now, RE just proxy the request it receive, but we have to do some things from now on. I prepared a json example that RE have to send to the API Url (I will change it in the next future, it would be good if the rule engine took it from the received payload)

{
'event': {
    'id': 'the-original-event-id',
    'nonce': 'a-nonce-in-microtime',
    'merchant_id': 'the-merchant-id',
    'customer_id': 'the-customer-id',
    'actions': {
               'pay':  {
                    'token_amount': 'token-amount-to-pay-to-client',
                    'client_address': 'client-wallet-address',
                    'message': 'message-to-send-in-transaction'
                },
               'mail': {
                    'message': 'email-message-to-client'
                },
               'push': {
                    'message': 'push-message-to-client'
               }
            }
       }
}

As you can see, the API backend expects to receive three type of actions: pay, mail and push. The RE can send only one of these or all three.

In the header it expects to receive these parameters to pass the login check security.

'API-Key:  publickey'
'API-Sign: signed-key'
sblabreu commented 3 years ago

New version 0.0.5 deployed, it supports event actions and reads the "redirect_url" from event payload, if not valid takes the URL from an environment variable. You can test this new logic with merchant "4321" for now only event action "pay" is mandatory, you can change that on the JSON schema.

The JSON schema for merchant 4321 expects and event like:

{
     "id": "fidelity:NIHlnz5Ftb",
     "redirect_url": "https://dashboard.fidelize.tk/index.php?r=ipn/rules",
     "merchant_id": "1234",
     "customer_id": "2",
     "order_number": "51612",
     "order_total": "99",
     "items": [{
         "product_id": "5",
         "product_name": "iPod Nano",
         "product_price": "99"
     }],
     "actions": {
         "pay": {
             "token_amount": "token-amount-to-pay-to-client",
             "client_address": "client-wallet-address",
             "message": "message-to-send-in-transaction"
         },
         "mail": {
             "message": "email-message-to-client"
         },
         "push": {
             "message": "push-message-to-client"
         }
     },
     "total_items": "1",
     "total_price": "15",
     "nonce": "1608810363358342",
     "client_address": "0xda9e2a20a018717d073a7acdfe93a099453b6844",
     "cart_id": "NIHlnz5Ftb"
 }