# API specs for Markets
This page describes details of API integration for Markets.
# Overview
The following information needs to be provided to Credify for Market integration.
- Scope definitions
- User counts API endpoint
- Offer evaluation API endpoint
- Encryption claims API endpoint
- Offer filtering API endpoint
# Skeleton service
We have developed a skeleton service to minimize the integration work, which handles most common logic and SDK interaction. We provide the skeleton service in Node.js, .NET, and Java as of May 2022. All you would have to do is to clone the repository and to customize some database interaction only.
The following sections relating to the backend implementation are not necessary if you use the skeleton service.
# Scope definitions
Each Market needs to configure scope definitions
ー so called data schema, 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 sharing. Service Providers will use the scope information to filter users on partner platforms to reach out to their potential customers.
# 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.
# 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.
- Generate a temporary token with your API key. This needs
organizataion
ororganization:introspect_token
scope. - 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 regard 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
, orAND_CONDITION
.
- This is one of
value: number or string (optional)
- Threshold value.
upper: number (optional)
- Secondary threshold value used for
IN_RANGE_CONDITION
.
- Secondary threshold value used for
claim: ClaimObject (optional)
- Claim information. This is described below.
value_type: string
- This is one of
TEXT
,FLOAT
,INTEGER
,BOOLEAN
, orOBJECT
.
- This is one of
subconditions: array<ConditionObject> (optional)
- If
kind
isAND_CONDITION
, it will containsubconditions
.
- If
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 Service Provider. In the following screen, this API will be called to evaluate what the offer detail will be.
# 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.
- Generate a temporary token with your API key. This needs
organizataion
ororganization:introspect_token
scope. - 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 Service Provider, 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.
- Generate a temporary token with your API key. This needs
organizataion
ororganization:introspect_token
scope. - 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
, andoffer_code
, signed with a public key of a Service Provider. Thisencryption_public_key
is going to be used to encrypt data exchange through OpenID Connect. Credify SDK will handle this token usage.
- This is a JWT whose payload is
approval_token: string
- This is a JWT whose payload is
client_id
,timestamp
,scopes
, andoffer_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.
- This is a JWT whose payload is
{
"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
andscopes
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.
- Generate a temporary token with your API key. This needs
organizataion
ororganization:introspect_token
scope. - 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
- Filter the offers list with a user's data sitting in your DB. This process will use
local_id
orcredify_id
in the request body. If therank
is 0, the offer is removed from the list. - Add
evaluation_result
to each offer.
rank (integer)
: This is same aslevel
aboverequested_scopes (string[])
: This is an array of string needed for redeeming an offerused_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 introspection 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 end-users. 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
, andaddress
.
scope_hash: string
- sha256 hashed value, like
CREDIT_SCORE_SCOPE_HASH
/EMAIL_SCOPE_HASH
in the above description.
- sha256 hashed value, like
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
, andtimestamp
, likeCREDIT_SCORE_CLAIM_TOKEN_PAYLOAD
/EMAIL_CLAIM_TOKEN_PAYLOAD
.
- JWT to ensure this data attachment is coming from a Market. Its payload is
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
- React Native
- Web
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
Markets will receive products that Service Providers cross offer. The SDK handles this offer related features.
- Kotlin
- Swift
- React Native
- Web
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
}
}
)
# Passport page
- Kotlin
- Swift
- React Native
This displays the result of referral.