# API specs for Markets

This page describes details of API integration for Markets.

TIP

This spec is for Markets who use the data sharing mechanism we provide.

If you do not use the data sharing feature, you can skip this page.

# 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

Integration model

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.

image

# API definition

API doc for serviceX integration

# 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.

image

# API definition

API doc for serviceX integration

# 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.

# API definition

API doc for serviceX integration

# Implementation with SDK

What you have to do by yourself is

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.

# API definition

API doc for serviceX integration

# 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, 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
  • 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.

Last Updated: 9/13/2022, 12:32:56 PM