# Set up to be Market

🔗 Registration

🔗 SDK installation

Let's upgrade your role to be Market on serviceX Dashboard.

image

In this process, the following information needs to be provided to Credify for integration.

  • Scopes definition
  • User counts API endpoint
  • Offer evaluation API endpoint
  • Encryption claims API endpoint
  • Offer filtering API endpoint

# Scope definitions

Each Market needs to configure scope definitions, which will state what piece of data the Market is going to share across the Credify network for what price. Of course the data transmission can never happen unless end-users make a consent for the data share. Service Providers will use these scope information to filter users to reach out to their potential users.

# User counts API endpoint - Backend

Since Credify possesses zero user data, Credify need to call API of Markets to know how many users fall under the specified threshold. This API is to be used in this component.

image

# Method

POST

# Authentication

This API is to be called by Credify with a JWT based access token. You need to validate the access token with token introspection API. This token introspection API requires you to have a token that is generated with your API key.

  1. Generate a temporary token with your API key. This needs organizataion or organization:introspect_token scope.
  2. Introspect the access token you get from the request. You will need to validate oidc_client:read_user_counts scope with this API.

This operation will be done in the Credify SDK. When you initialize the SDK, the #1 will be handled automatically. With regards to the #2, you just need to call a function in the SDK.

# Request Body

The request body has 2 properties:

  • conditions: (Condition || null)[]
  • required_custom_scopes: string[][]

Conditions have several types:

  • Value Conditions
    • Contain Condition (CONTAIN_CONDITION)
    • In Range Condition (IN_RANGE_CONDITION)
    • Larger Than Condition (LARGER_THAN_CONDITION)
    • Larger Than Equal Condition (LARGER_THAN_OR_EQUAL_CONDITION)
    • Less Than Condition (LESS_THAN_CONDITION)
    • Less Than Equal Condition (LESS_THAN_OR_EQUAL_CONDITION)
    • Equality Condition (EQUALITY_CONDITION)
  • Logical Conditions
    • And Condition (AND_CONDITION)

The Value Conditions have

  • kind
  • claim
  • value
  • upper

The Logical Conditions have

  • kind
  • subconditions

Each property's description:

  • kind: string
    • This is one of CONTAIN_CONDITION, IN_RANGE_CONDITION, LARGER_THAN_CONDITION, LARGER_THAN_OR_EQUAL_CONDITION, LESS_THAN_CONDITION, LESS_THAN_OR_EQUAL_CONDITION, EQUALITY_CONDITION, or AND_CONDITION.
  • value: number or string (optional)
    • Threshold value.
  • upper: number (optional)
    • Secondary threshold value used for IN_RANGE_CONDITION.
  • claim: ClaimObject (optional)
    • Claim information. This is described below.
    • value_type: string
      • This is one of TEXT, FLOAT, INTEGER, BOOLEAN, or OBJECT.
  • subconditions: array<ConditionObject> (optional)
    • If kind is AND_CONDITION, it will contain subconditions.

serviceX supports multi-level offers for Service Providers to differentiate the offers for end-user based on their data.

When a Service Provider creates a new campaign, it will need to configure levels. The number of levels is equal with the number top-level offer conditions. If an offer condition is nested through a logical condition having several sub conditions, the top-level offer condition will be recognized as 1 condition.

The first item of offer conditions represents a condition of level 1 (the lowest level), while the subsequent items will represent following levels.

The following example has 2 top-level offer conditions, meaning there are 2 levels. In some cases a top-level offer condition could be null value and could be interpreted as no condition for that level and will always be true.

The number of required_custom_scopes and the number conditions is same.

{
  "conditions": [
    {
      "kind": "AND_CONDITION",
      "subconditions": [
        {
          "kind": "CONTAIN_CONDITION",
          "claim": {...},
          "value": "VNM"
        },
        {
          "kind": "IN_RANGE_CONDITION",
          "claim": {...},
          "value": "120",
          "upper": "300"
        }
      ]
    },
    {
      "kind": "IN_RANGE_CONDITION",
      "claim": {...},
      "value": "120",
      "upper": "300"
    }
  ],
  "required_custom_scopes": [
    ["697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"], // requires `finscore-profile` scope for rank 1 beside `finscore-score` scope in condition above  
    []                                                         // no additional required scopes for rank 2
  ]
}

This is an example of request body.

{
  "conditions": [
    {
      "kind": "LARGER_CONDITION",
      "value": 200,
      "claim": {
        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
        "name": "597f6eca-6276-4993-bfeb-53cbbbba6f08:credit-score",
        "value_type": "INTEGER",
        "min_value": 100,
        "max_value": 300,
        "scope": {
          "name": "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-score"
        }
      }
    },
    {
      "kind": "AND_CONDITION",
      "subconditions": [
        {
          "kind": "IN_RANGE_CONDITION",
          "value": 20,
          "upper": 40,
          "claim": {
            "id": "797f6eca-6276-4993-bfeb-53cbbbba6f08",
            "name": "897f6eca-6276-4993-bfeb-53cbbbba6f08:age",
            "value_type": "INTEGER",
            "min_value": 0,
            "max_value": 120,
            "scope": {
              "name": "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"
            }
          }
        },
        {
          "kind": "CONTAIN_CONDITION",
          "value": "VNM",
          "claim": {
            "id": "997f6eca-6276-4993-bfeb-53cbbbba6f08",
            "name": "097f6eca-6276-4993-bfeb-53cbbbba6f08:country",
            "value_type": "TEXT",
            "scope": {
              "name": "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"
            }
          }
        }
      ]
    }
  ]
}

# Response Body

  • counts: array<number>
    • This contains the number of people who are qualified for each specified condition. Each condition is independent.
    • The number of items is equal with the number top-level offer conditions.
{
  "data": {
    "counts": [12340, 144]
  }
}

# Implementation with SDK

What you have to do by yourself is

  • Count the number of users who meet the offer conditions passed from the request
  • Node.js
  • Java
  • .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");

// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";

const app = express();
app.use(bodyParser.json());
app.use(cors());

app.post("/user-count", async (req, res) => {
  // mode is `sandbox` or `production`
  const credify = await Credify.create(signingPrivateKey, apiKey, {
    mode: "sandbox",
  });

  // Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
  const token = extractToken(req);
  const isValidToken = await credify.auth.introspectToken(
    token,
    "oidc_client:read_user_counts"
  );
  if (!isValidToken) {
    return res.status(401).send({ message: "Unauthorized" });
  }

  const conditions = req.body.conditions;

  if (!conditions) {
    return res.status(400).send({ message: "Invalid body" });
  }
  
  // Your current users
  const users = ...

  // do your calculation with `conditions`
  let counts = Array(conditions.length).fill(0)
  await conditions.forEach(async (c, index) => {
    if (Object.keys(c).length === 0) {
      counts[index] = users.length
    } else {
      await users.forEach(async (u) => {
        // You user's claim composed object
        const userClaims = ...
        //*** Our SDK already supports offer evaluation for you
        const res = await credify.offer.evaluateOffer(
          [c],
          requiredCustomScopes,
          userClaims
        )
        if (res.rank === 1) {
          counts[index] += 1
        }
      })
    }
  })

  const response = {
    data: {
      counts,// return the calculation result
    },
  };
  res.json(response);
});

# Offer evaluation API endpoint - Backend

This API is needed for users to determine which piece of data they are going to share with a data receiving service. In the following screen, this API will be called to evaluate what the offer detail will be.

image

# Method

POST

# Authentication

This API is to be called by Credify with a JWT based access token. You need to validate the access token with token introspection API. This token introspection API requires you to have a token that is generated with your API key.

  1. Generate a temporary token with your API key. This needs organizataion or organization:introspect_token scope.
  2. Introspect the access token you get from the request. You will need to validate individual:read_evaluated_offer scope with this API.

# Request Body

  • conditions: array<ConditionObject>
    • This contains conditions of an offer. Described above.
  • credify_id: string
    • This represents a Credify user ID.
  • scopes: array<string>
    • This is a scope list that users are going to share.
  • required_custom_scopes: array<array<string>>
    • This is a custom scopes list that are required for this offer in each rank.

This is an example of request body.

{
  "conditions": [
    {
      "kind": "LARGER_CONDITION",
      "value": 200,
      "claim": {
        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
        "name": "597f6eca-6276-4993-bfeb-53cbbbba6f08:credit-score",
        "value_type": "INTEGER",
        "min_value": 100,
        "max_value": 300,
        "scope": {
          "name": "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-score"
        }
      }
    },
    {
      "kind": "AND_CONDITION",
      "subconditions": [
        {
          "kind": "IN_RANGE_CONDITION",
          "value": 20,
          "upper": 40,
          "claim": {
            "id": "797f6eca-6276-4993-bfeb-53cbbbba6f08",
            "name": "897f6eca-6276-4993-bfeb-53cbbbba6f08:age",
            "value_type": "INTEGER",
            "min_value": 0,
            "max_value": 120,
            "scope": {
              "name": "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"
            }
          }
        },
        {
          "kind": "CONTAIN_CONDITION",
          "value": "VNM",
          "claim": {
            "id": "997f6eca-6276-4993-bfeb-53cbbbba6f08",
            "name": "097f6eca-6276-4993-bfeb-53cbbbba6f08:country",
            "value_type": "TEXT",
            "scope": {
              "name": "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"
            }
          }
        }
      ]
    }
  ],
  "credify_id": "123f6eca-6276-4993-bfeb-53cbbbba6f08",
  "scopes": [
    "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile",
    "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-score"
  ],
  "required_custom_scopes": [
    ["697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"], // requires `finscore-profile` scope for rank 1 beside `finscore-score` scope in condition above  
    []                                                         // no additional required scopes for rank 2
  ]
}

# Response Body

  • rank: number
    • This represents what level this user falls under.
  • requested_scopes: array<string>
    • This is an array of scope names that are needed for this offer.
  • used_scopes: array<string>
    • This is an array of scope names that are used to get this level.
{
  "data": {
    "rank": 2,
    "requested_scopes": [
      "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile",
      "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-score"
    ],
    "used_scopes": ["697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile"]
  }
}

# Implementation with SDK

What you have to do by yourself is

  • Find the user that is associated with the credify ID passed from the request
  • Evaluate the user's level based on their profile
  • Node.js
  • Java
  • .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");

// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";

const app = express();
app.use(bodyParser.json());
app.use(cors());

app.post("/offer-evaluation", async (req, res) => {
  // mode is `sandbox` or `production`
  const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });

  // Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
  const token = extractToken(req);
  const isValidToken = await credify.auth.introspectToken(token, "individual:read_evaluated_offer");
  if (!isValidToken) {
    return res.status(401).send({ message: "Unauthorized" });
  }

  if (!req.body.credify_id || !req.body.conditions || !req.body.scopes) {
    return res.status(400).send({ message: "Invalid body" });
  }

  // 1. Use credify_id to find an user in your system associated with this ID
  const user = ...;

  const conditions = req.body.conditions || [{}]

  const requiredCustomScopes = req.body.required_custom_scopes || []

  // 2. Do your evaluation with `conditions` and `requiredCustomScopes`
  // Your user's claims
  const allUserClaims = ...

  // This is scope that user want to share:
  const sharedScopes = req.body.scopes

  // The claims that user want to share
  let userSharedClaims = {}
  for (let scope of sharedScopes) {
    userSharedClaims[scope] = allUserClaims[scope]
  }

  //*** Our SDK already supports offer evaluation for you
  const result = await credify.offer.evaluateOffer(
    conditions,
    requiredCustomScopes,
    userSharedClaims
  )

  const response = {
    data: {
      rank: result.rank,
      used_scopes: result.usedScopes,
      requested_scopes: result.requestedScopes,
    }
  };
  res.json(response);
});

# Encrypted claims API endpoint - Backend

When a user is going to share his/her data with a data receiving service, he/she will make a consent on the Credify's consent page. After the successful consent, Credify will call this encrypted claims API to retrieve the encrypted claim values that are encrypted with the Service Provider's ephemeral public key. Once Credify obtains the encrypted values, it will pass them to the data receiver through OpenID Connect.

# Authentication

This API is to be called by Credify with a JWT based access token. You need to validate the access token with token introspection API. This token introspection API requires you to have a token that is generated with your API key.

  1. Generate a temporary token with your API key. This needs organizataion or organization:introspect_token scope.
  2. Introspect the access token you get from the request. You will need to validate oidc_client:read_encrypted_claims scope with this API.

# Method

POST

# Request Body

  • user_id: string
    • Subject Credify ID.
  • request_token: string
    • This is a JWT whose payload is encryption_public_key, timestamp, scopes, and offer_code, signed with a public key of a Service Provider. This encryption_public_key is going to be used to encrypt data exchange through OpenID Connect. Credify SDK will handle this token usage.
  • approval_token: string
    • This is a JWT whose payload is client_id, timestamp, scopes, and offer_code, signed with a public key of an end-user. This represents who gets approval to access what data through what offer. Credify SDK will handle this token usage.
{
  "user_id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  "request_token": "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0...",
  "approval_token": "eyJhbGwi8iKlDI1NTE5lawidHlwIo1lmldpOn9..."
}

# Response Body

Each field under claims property is encrypted under a Service Provider's public key passed in the request body. This response will contain all the encrypted scopes that are stated in approval_token. If the approval_token contains scope A, scope B, and scope C, then this response will have 3 objects. verification_info is necessary for Credify to add verification flag to claim objects.

Commitment is a secret random value that will increase an entropy of a message, and this commitment is to be generated for each scope and kept secret by each Market.

Credify provides you with an SDK to manage this encryption operation.

{
  "data": {
    "verification_info": {
      "email": {
        "email": "plain text email address"
      },
      "profile": {
        "name": "name in plain text",
        "family_name": "family name in plain text",
        "given_name": "given name in plain text",
        "middle_name": "middle name in plain text"
      },
      "phone": {
        "phone_number": "0381234123",
        "country_code": "+84"
      }
    },
    "claims": {
      "phone": {
        "phone_number": "encrypted phone number",
        "phone_number_commitment": "encrypted commitment"
      },
      "email": {
        "email": "encrypted email",
        "email_commitment": "encrypted commitment"
      },
      "profile": {
        "name": "encrypted name",
        "family_name": "encrypted family name",
        "given_name": "encrypted given name",
        "middle_name": "encrypted middle name",
        "gender": "encrypted gender",
        "birthdate": "encrypted birth date",
        "profile_commitment": "encrypted commitment"
      },
      "address": {
        "address": {
          "formatted": "encrypted formatted address",
          "street_address": "encrypted street address",
          "locality": "encrypted locality",
          "region": "encrypted region",
          "postal_code": "encrypted postal code",
          "country": "encrypted country"
        },
        "address_commitment": "encrypted commitment"
      },
      "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-score": {
        "597f6eca-6276-4993-bfeb-53cbbbba6f08:credit-score": "encrypted claim value",
        "597f6eca-6276-4993-bfeb-53cbbbba6f08:commitment": "encrypted commitment"
      },
      "697f6eca-6276-4993-bfeb-53cbbbba6f08:finscore-profile": {
        "897f6eca-6276-4993-bfeb-53cbbbba6f08:age": "encrypted claim value",
        "897f6eca-6276-4993-bfeb-53cbbbba6f08:commitment": "encrypted commitment"
      }
    }
  }
}

# Implementation with SDK

What you have to do by yourself is

  • Find the user that is associated with the credify ID passed from the request
  • Compose a claim object with user and scopes

The below code is an example of how a claim object composer look like. We recommend you to follow the format of the object below so a service provider can decrypt the claims given from your side without any unexpected issues. All the claim values pass to the claim object composer must be converted to a string.

 const claims = {};
 claims["b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data"] = {
    "3285592c-9aaf-4182-bff5-941ce5dac483:purchase-count": `${user.transactionsCount}`,
    "3285592c-9aaf-4182-bff5-941ce5dac483:total-payment-amount": `${user.totalPaymentAmount}`,
    "b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data:commitment": commitments ? commitments["b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data"] : undefined
 };
 claims["b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score"] = {
    "3285592c-9aaf-4182-bff5-941ce5dac483:market-name%20score": `${user.creditScore}`,
    "b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score:commitment": commitments ? commitments["b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score"] : undefined
 };
  • Node.js
  • Java
  • .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");

// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";

const app = express();
app.use(bodyParser.json());
app.use(cors());

app.post("/encrypted-claims", async (req, res) => {
  // mode is `sandbox` or `production`
  const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });

  // Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
  const accessToken = extractToken(req);
  if (accessToken === "") {
    return res.status(401).send({ message: "Unauthorized" });
  }
  if (!req.body.user_id || !req.body.request_token || !req.body.approval_token) {
    return res.status(400).send({ message: "Invalid body" });
  }

  const credifyId = req.body.user_id;
  const requestToken = req.body.request_token;
  const approvalToken = req.body.approval_token;
  const { publicKey, scopes } = await credify.claims.validateRequest(accessToken, requestToken, approvalToken);

  // 1. Use `credifyId` to find an user in your system associated with `credifyId`
  const user = ...;

  // 2. The commitment for user claim that already created before
  const commitment =

  // 3. Compose a claim object with `user` and `commitment`
  const claims = ...;
  
  const encrypted = await credify.claims.encrypt(claims, publicKey)

  // 4. Attach verification_info for verifying user's info
  const data = {
    data: {
      verification_info: {
        phone: {
          phone_number: user.phoneNumber,
          country_code: user.phoneCountryCode,
        },
        email: {
          email: user.email,
        },
        profile: {
          family_name: user.lastName,
          given_name: user.firstName,
        },
      },
      claims: encrypted,
    },
  }
  res.json(data);
});

# Offer filtering API endpoint - Backend

This returns a list of available offers for each user with evaluation result. The offers will be displayed through Credify's SDK on your platform, therefore this API implementation is needed for the SDK to handle them efficiently.

# Method

POST

# Authentication

This API is to be called by Credify with a JWT based access token. You need to validate the access token with token introspection API. This token introspection API requires you to have a token that is generated with your API key.

  1. Generate a temporary token with your API key. This needs organizataion or organization:introspect_token scope.
  2. Introspect the access token you get from the request. You will need to validate claim_provider:read_filtered_offers scope with this API.

# Request Body

  • local_id: string | undefined
    • This is a unique identifier in the Market.
  • credify_id: string | undefined
    • This is a unique identifier of Credify.
  • offers: array<OfferObject>
    • This contains offer conditions, which is checked with user's data in the Market.
{
  "local_id": "abcd",
  "credify_id": "123f6eca-6276-4993-bfeb-53cbbbba6f08",
  "offers": [
    {
      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
      "code": "string",
      "campaign_id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
      "conditions": [
        {
          "claim": {
            "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
            "name": "string",
            "value_type": "FLOAT",
            "min_value": 0,
            "max_value": 0,
            "scope": {
              "name": "string"
            }
          },
          "kind": "string",
          "value": "string"
        }
      ],
      "required_custom_scopes": [
        ["string"],
        ["string", "string"]
      ]
    }
  ]
}

# Response Body

What you will do to form this response is

  1. Filter the offers list with a user's data sitting in your DB. This process will use local_id or credify_id in the request body. If the rank is 0, the offer is removed from the list.
  2. Add evaluation_result to each offer.
  • rank (integer): This is same as level above
  • requested_scopes (string[]): This is an array of string needed for redeeming an offer
  • used_scopes (string[]): This is an array of string required for getting this rank.
"evaluation_result": {
  "rank": 1,
  "requested_scopes": [
    "string"
  ],
  "used_scopes": [
    "string"
  ]
}

This is an example of a whole response body.

{
  "data": {
    "offers": [
      {
        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
        "code": "string",
        "campaign_id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
        "conditions": [
          {
            "claim": {
              "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
              "name": "string",
              "value_type": "FLOAT",
              "min_value": 0,
              "max_value": 0,
              "scope": {
                "name": "string"
              }
            },
            "kind": "string",
            "value": "string"
          }
        ],
        "required_custom_scopes": [
          ["string"],
          ["string", "string"]
        ],
        "evaluation_result": {
          "rank": 1,
          "used_scopes": ["string"],
          "requested_scopes": ["string"]
        }
      }
    ]
  }
}

# Implementation with SDK

What you have to do by yourself is

  • Find the user that is associated with the credify ID or local ID passed from the request
  • Evaluate the user's level based on their profile and return this evaluation result along with the offers.
  • Node.js
  • Java
  • .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");

// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";

const app = express();
app.use(bodyParser.json());
app.use(cors());

app.post("/offer-filtering", async (req, res) => {
  // mode is `sandbox` or `production`
  const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });

  // Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
  const token = extractToken(req);
  const isValidToken = await credify.auth.introspectToken(token, "claim_provider:read_filtered_offers");
  if (!isValidToken) {
    return res.status(401).send({ message: "Unauthorized" });
  }
  const credifyId = req.body.credify_id;
  const localId = req.body.local_id;
  const offers = req.body.offers;
  if (!credifyId && !localId) {
    return res.status(400).send({ message: "No ID found" });
  }

  if (offers === undefined) {
    const response = {
      data: {
        offers: [],
      },
    };
    return res.status(200).json(response);
  }

  // 1. Use `credifyId` or `localId` to find an user in your system. `localId` means ID in your system.
  const user = ...;

  // 2. Personalize the offers with `user`.
  const personalizedOffers = []
  await offers.forEach(async (offer) => {
    const userClaims = composeClaimObject(u)
    //*** Our SDK already supports offer evaluation for you
    const result = await credify.offer.evaluateOffer(
      offer.conditions,
      offer.required_custom_scopes,
      userClaims
    )

    const formattedOffer = {
      ...offer,
      evaluation_result: {
        rank: result.rank,
        used_scopes: result.usedScopes,
        requested_scopes: result.requestedScopes,
      },
    }

    if (result.rank > 0) {
      // Return only qualified offers
      personalizedOffers.push(formattedOffer)
    }
  })

  const response = {
    data: {
      offers: personalizedOffers,
    },
  }
  res.json(response);
});

In case you want to have more flexible when introspecting the token (Eg: testing the API without forcing scope usage strictly or using the integration test), we have introspectTokenReturnResult function returns the introspect result like that:


  const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });

  // Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
  const token = extractToken(req);
  const introspectResult = await credify.auth.introspectTokenReturnResult(
    token
  );
  let validToken;
  if (
    introspectResult.data &&
    introspectResult.data.active &&
    introspectResult.data.scope.includes("claim_provider")
  ) {
    validToken = true;
  }

  if (!validToken) {
    return res.status(401).send({ message: "Unauthorized" });
  }

# Data provision - Backend

It's almost there! Lastly, you have to set up one API for endusers. This process will allow Service Providers to verify the data is coming from your system without being tampered. The easiest way is making an API endpoint and calling it every time your users open serviceX SDK.

# Sandbox
[POST] https://sandbox-api.credify.dev/v1/claim-providers/entities/{id}/claims

# Production
[POST] https://api.credify.one/v1/claim-providers/entities/{id}/claims
Request header:
- "Authorization": YOUR_TEMPORARY_TOKEN

This is how to compose a payload in a claim token.

// In the case of your `custom` claims
const CREDIT_SCORE_SCOPE_DATA = {
  YOUR_CLAIM_A: "CLAIM_A_VALUE",
  YOUR_CLAIM_B: "CLAIM_B_VALUE",
  YOUR_CLAIM_C: "CLAIM_C_VALUE",
  "credit-score:commitment": "SECRET_RANDOM_COMMITMEMT",
};
const CREDIT_SCORE_SCOPE_HASH = sha256(JSON(CREDIT_SCORE_SCOPE_DATA));
const CREDIT_SCORE_CLAIM_TOKEN_PAYLOAD = {
  scope_name: "credit-score",
  user_id: "a6367e50-2d4c-...", // passed from the mobile SDK
  scope_hash: CREDIT_SCORE_SCOPE_HASH,
  timestamp: 1612197298,
};

// In the case of your `standard` claims
const EMAIL_SCOPE_DATA = {
  email: "test@credify.one",
  email_commitment: "SECRET_RANDOM_COMMITMEMT",
};
const EMAIL_SCOPE_HASH = sha256(JSON(CREDIT_SCORE_SCOPE_DATA));
const EMAIL_CLAIM_TOKEN_PAYLOAD = {
  scope_name: "email",
  user_id: "a6367e50-2d4c-...", // passed from the mobile SDK
  scope_hash: EMAIL_SCOPE_HASH,
  timestamp: 1612197298,
};

Once you generate a claim token, you need to make a request body with the following properties:

  • scope_name: string
    • Unique scope name registered in serviceX Dashboard
    • In case of standard claims, this will be predefined, such as profile, phone, email, and address.
  • scope_hash: string
    • sha256 hashed value, like CREDIT_SCORE_SCOPE_HASH / EMAIL_SCOPE_HASH in the above description.
  • timestamp: integer
    • Unix timestamp in sec
  • user_claim_token: string
    • JWT to ensure this data attachment is coming from a Market. Its payload is provider_id, user_id, scope_name, scope_hash, and timestamp, like CREDIT_SCORE_CLAIM_TOKEN_PAYLOAD / EMAIL_CLAIM_TOKEN_PAYLOAD.

This may look complicated, but don't worry. Credify's SDK is handling everything 👍.

# Implementation with SDK

What you have to do by yourself is

  • Find the user that is associated with the internal ID passed from the request
  • Save the credify ID along with the user
  • Compose a claim object with this user's data stored in your DB
  • Save the commitments, which are returned by the SDK's function, in your DB
  • Node.js
  • Java
  • .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");

// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";
const id = "YOUR_ORGANIZATION_ID";

const app = express();
app.use(bodyParser.json());
app.use(cors());


app.post("/claims", async (req, res) => {
  // mode is `sandbox` or `production`
  const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });

  const internalId = req.body.id;
  const credifyId = req.body.credify_id;

  // 1. Find a user with `internalId`
  const user = ...;

  // 2. Update the user to save `credifyId` in your DB

  // 3. Compose a claim object with this user's data stored in your DB
  const claims = ...;
  // This is an example:
  // const claims = {
  //   "phone": {
  //     "phoneNumber": "+84381234567"
  //   },
  //   "credit-score": {
  //     "scope": 124,
  //     "fraud": false,
  //     "updatedAt": "2021-02-16 10:25:14.599+07"
  //   },
  //   "transactions": {
  //     "totalCount": 142,
  //     "totalPaymentAmount": 8911,
  //     "currency": "usd"
  //   }
  // }


  // 4. Send the digest data of the claims and obtain the commitments
  const commitments = await credify.claims.push(id, credifyId, claims);

  // 5. Save the commitments in your DB

  return res.status(200).json(response);
});

That's it on the Backend side!

# Mobile integration

Credify provides Android and iOS SDK.

# Set up

Here is how to set up the mobile SDK. The installation is described here.

  • Kotlin
  • Swift
  • Flutter

If you have created a class that extends from Application class. You only add the below code to the onCreate() method. You can generate a new API key on serviceX Dashboard.

override fun onCreate() {
    super.onCreate()
    ...
    CredifySDK.Builder()
        .withApiKey([Your API Key])
        .withContext(this)
        .withEnvironment([Environment])
        .build()
    ...
}

Otherwise, you will need to create a new class that extends from Application class. In this example, it is named as DemoApplication.

...
class DemoApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // Generates a singleton here
        CredifySDK.Builder()
            .withApiKey([Your API Key])
            .withContext(this)
            .withEnvironment([Environment])
            .build()
    }
    ...
}

AND update your AndroidManifest.xml file.

<application
    android:name=".DemoApplication"
    ...
    >
    ...
</application>

After creating the CredifySDK instance, you can access the singleton like following:

val credifySDK = CredifySDK.instance

# Offer management

Market will receive products that Service Providers cross offer. The SDK handles this offer related features.

  • Kotlin
  • Swift
  • Flutter

First, you need to create a parameter object by using GetOfferListParam class.

Secondly, to get offers list from the Credify SDK, you should use getOfferList method:

val params = GetOfferListParam(
    phoneNumber = // Logged in user's phone number (Optional) - e.g. "32123456789",
    countryCode = // Logged in user's phone country code (Optional) - e.g. "+84",
    localId = // Logged in user's ID in your service,
    credifyId = // Logged in user's credify ID (Optional)
    productTypes = // Product types as string array
)

CredifySDK.instance.getOfferList(params = params, callback: OfferListCallback)
// OR
CredifySDK.instance.getOfferList(params = params): Observable<List<Offer>>

When a user wants to use one of the offers, you will do the following:

// Create one.credify.sdk.core.model.UserProfile object
val user = UserProfile(
    id = // Logged in user's ID in your service,
    name = Name(
        firstName = // Logged in user's first name,
        lastName = // Logged in user's last name,
        middleName = // Logged in user's middle name (Optional),
        name = // Logged in user'ss full name (Optional),
        verified = // Is logged in user's name verified?
    ),
    phone = Phone(
        phoneNumber = // Logged in user's phone number,
        countryCode = // Logged in user's phone country code,
        verified = // Is logged in user's phone number verified?
    ),
    email = // Logged in user's email,
    dob = // Logged in user's date of birth (Optional),
    address = // Logged user's address (Optional)
)

// Display an offer detail:
CredifySDK.instance.offerApi.showOffer(
    context = // Context,
    offer = // one.credify.sdk.core.model.Offer object,
    userProfile = // one.credify.sdk.core.model.UserProfile object,
    credifyId = // Logged in user's credify ID. If your user have created Credify account then it should not be null,
    marketName = // Your app name,
    pushClaimCallback = // CredifySDK.PushClaimCallback callback,
    offerPageCallback = // CredifySDK.OfferPageCallback callback
)

You will have to handle CredifySDK.PushClaimCallback for calling the data digest provisioning API. In addition to this, you will have to handle CredifySDK.OfferPageCallback, which is called when the offer detail page is closed.

CredifySDK.instance.offerApi.showOffer(
    context = // Context,
    offer = // one.credify.sdk.core.model.Offer object,
    userProfile = // one.credify.sdk.core.model.UserProfile object,
    credifyId = // Logged in user's credify ID,
    marketName = // Your app name,
    pushClaimCallback = object : CredifySDK.PushClaimCallback {
        override fun onPushClaim(
            credifyId: String,
            user: UserProfile,
            resultCallback: CredifySDK.PushClaimResultCallback
        ) {
            // Code for calling your API to send data digest.
            // After the API successful API request, you will have to notify the Credify SDK. For example:
            resultCallback.onPushClaimResult(
                isSuccess = [true if success. Otherwise, pass false]
            )
        }
    },
    offerPageCallback = object : CredifySDK.OfferPageCallback {
        override fun onClose() {
            // Your code logic here
        }
    }
)

# Offer result page

After the offer is used, the SDK may display the result page.

  • Kotlin

This displays the result of referral.

CredifySDK.instance.referralApi.showReferralResult(
    context = // Context,
    userProfile = // one.credify.sdk.core.model.UserProfile object,
    marketName = // Your app name,
    callback = object : CredifySDK.OnShowReferralResultCallback {
        override fun onShow() {
            // The page is showing on the UI
        }

        override fun onError(ex: Exception) {
            // There is an error
        }

        override fun onClose() {
            // The page is closed
        }
    }
)
Last Updated: 11/16/2021, 10:29:23 AM