NAV Navbar
shell java javascript
  • Overview
  • Authentication
  • Transactions
  • Refunds
  • Webhook
  • Error Codes
  • Overview

    The ChangeJar Online Payments Gateway allows e-commerce merchants and applications to accept secure online payments for small (nominally < $200) purchases, from a consumer’s ChangeJar balance. The payee approves the transaction using either the ChangeJar app, or the ChangeJar Pay extension for Facebook Messenger.

    The API provides an endpoint to request a payment, and calls a webhook when the payment is completed (or declined, or rejected). The webhook URL is specified at setup time.

    The API follows REST conventions, and uses JSON as the data interchange format. When invoking the webhook, the JSON is sent in JWT format, to validate the authenticity of the data.

    Example Code

    The ChangeJar Payments API is really easy to use, and we've put together a simple Express-based server that illustrates how to request a payment. Check out our demo project.

    Authentication

    Merchants are authenticated using the “single-headed” variant of OAuth2.0. This requires that the merchant’s e-commerce server request an OAuth bearer token before initiating the transaction. The token is then included in the header of every request.

    OAuth tokens expire after a period of time. The merchant’s server should handle a failed token response by requesting a fresh OAuth token and then repeating the failed request, using the new token.

    Get an OAuth Token

    To get an OAuth token, use this code:

    curl https://sandbox.changejar.com/oauth/token \
        -X POST \
        -H "Accept: application/json" \
        -u clientId:clientSecret \
        -d "grant_type=password" \
        -d "scope=read write" \
        -d "username=myUserName" \
        -d "password=myPassword"
    
    // Using UniREST (http://unirest.io/java.html)
    HttpResponse<JsonNode> response = Unirest.post(baseUrl + "/oauth/token")
            .basicAuth(clientId, clientSecret)
            .field("grant_type", "password")
            .field("scope", "read write")
            .field("username", myUserName)
            .field("password", myPassword)
            .asJson();
    
    if (response.getStatus() == 200) {
        oauthToken = response.getBody().getObject().getString("access_token");
    }
    
    const response = await fetch(baseUrl + '/oauth/token', {
        method: 'post',
        credentials: 'omit',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Authorization': "Basic " + credentials
        },
        body: "grant_type=password&scope=read%20write&username=" + myUserName + "&password=" + myPassword
    })
    
    if (response.status == 200) {
      const tokenPayload = await parseJSON(response)
      const oauthToken = tokenPayload.access_token
    }
    

    Make sure to replace clientId, clientSecret, myUserName and myPassword with your assigned username and password.

    Sample response:

    {
        "access_token": "12345678-1234-1234-1234-123456789abc",
        "token_type": "bearer",
        "expires_in": 42000,
        "scope": "read write"
    }
    

    Request

    POST /oauth/token

    Query Parameters

    Parameter Type Required Description
    grant_type String Yes The constant string "password"
    scope String Yes The contstant string "read write"
    username String Yes Your assigned username
    password String Yes Your assigned password

    Returns

    HTTP status 200 Ok is returned upon success, along with a JSON body.

    Result Fields

    Field Type Always Present Description
    access_token String Yes The OAuth token to be used for subsequent API requests
    token_type String Yes The constant string "bearer"
    expires_in Integer Yes The number of seconds until the OAuth token expires
    scope String Yes The constant string "read write"

    The value returned for access_token should be included as a header for all subsequent API requests as follows:

    Authorization: Bearer 12345678-1234-1234-1234-123456789abc

    Errors

    Failure to obtain an OAuth token will return a 401 Unauthorized. See Errors for details.

    Transactions

    Request a Transaction

    To request a transaction, use this code:

    curl https://sandbox.changejar.com/payments/v1/transactions \
        -X POST \
        -H "Authorization: Bearer myOAuthToken" \
        -H "Content-Type: application/json;charset=UTF-8" \
        -d '{
            "reference": "B37641",
            "amount": 2375,
            "expiresIn": 120,
            "data": "transid=37641"
         }'
    
    // Using UniREST (http://unirest.io/java.html)
    JSONObject body = new JSONObject()
        .put("reference", "B37641")
        .put("amount", 2375)
      .put("description", "This is my product description")
        .put("expiresIn", 1200)
        .put("data", "transid=37641");
    
    HttpResponse<JsonNode> response = Unirest.post(baseUrl + "/payments/v1/transactions")
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer myOAuthToken")
        .body(body)
        .asJson();
    
    const transactionRequest = {
        amount: 2375
        reference: "B37641",
        description: "This is my product description",
        expiresIn: 120,
        data: "transid=37641"
    }
    
    const response = await fetch(baseUrl + '/payments/v1/transactions', {
        method: 'post',
        credentials: 'omit',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': 'Bearer myOAuthToken'
        },
        body: JSON.stringify(transactionRequest)
    })
    
      if (response.status == 201) {
        const paymentResult = await parseJSON(response)
      }
    

    Make sure to replace myOAuthToken with the OAuth token retrieved from the /oauth/token endpoint.

    Sample response:

    {
        "id": 2417,
        "appUrl": "https://qr.changejar.com/t/6059521234567890123",
        "messengerUrl": "https://m.me/changejar?ref=6059521234567890123",
    }
    

    Request

    POST /payments/v1/transactions

    Request Body Parameters

    Parameter Type Required Description
    amount Integer Yes The transaction amount, in cents
    reference String Yes An identifier string of up to 64 characters which will be used to identify the request when the webhook is called to indicate completion or failure
    description String No An optional description that will be displayed to the user
    expiresIn Integer No The lifetime of the request, in seconds.
    data String No A string of up to 1024 characters to store information relevant to the merchant

    Returns

    HTTP status 201 Created is returned upon success, along with a JSON body.

    Result Fields

    Field Type Always Present Description
    appUrl String Yes A URL which will open the ChangeJar application to authorize the payment
    messengerUrl String Yes A URL which will open a conversation in ChangeJar Pay for Facebook Messenger to authorize the payment

    Errors

    Errors related to a missing or invalid OAuth token will return a 401 Unauthorized.

    Errors due to an invalid request will return a 422 Unprocessable Entity, with possible error codes 1016, 1049, 1800, and 1801.

    See Errors for details.

    Cancel a Transaction

    To cancel a transaction, use this code:

    curl https://sandbox.changejar.com/pos/v1/transactions
        -X DELETE \
        -H "Authentication: Bearer myOAuthToken" \
        -H "Content-Type: application/json;charset=UTF-8" \
        -d 'reference=B37641'
    
    // Using UniREST (http://unirest.io/java.html)
    HttpResponse<JsonNode> response = Unirest.delete(baseUrl + "/payments/v1/transactions")
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer myOAuthToken")
        .parameter("reference", "B37641")
        .asJson();
    
    fetch(baseUrl + '/payments/v1/transactions?reference=B37641', {
        method: 'delete',
        credentials: 'omit',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': 'Bearer myOAuthToken'
        }
    })
    

    Make sure to replace myOAuthToken with the OAuth token retrieved from the /oauth/token endpoint.

    Request

    DELETE /payments/v1/transactions

    Request Body Parameters

    Parameter Type Required Description
    reference String Yes The same reference that was used to request the transaction

    Returns

    HTTP status 204 No content is returned upon success.

    Errors

    Errors related to a missing or invalid OAuth token will return a 401 Unauthorized.

    Errors due to an invalid request will return a 422 Unprocessable Entity, with possible error codes 1032, 1800, and 1801.

    See Errors for details.

    Refunds

    A refund returns some or all of a previous purchase transaction to a consumer. Multiple refunds can be issued against one purchase transaction, however you can not refund more than what the consumer paid in the original transaction. It is important to note that if any rewards were paid on the original purchase transaction, they will be forfeited entirely by refunding even part of the transaction.

    Request a Refund

    To request a refund, use this code:

    curl https://sandbox.changejar.com/payments/v1/refunds \
        -X POST \
        -H "Authorization: Bearer myOAuthToken" \
        -H "Content-Type: application/json;charset=UTF-8" \
        -d '{
            "refundId": "C7231"
            "reference": "B37641",
            "amount": 2375
         }'
    
    // Using UniREST (http://unirest.io/java.html)
    JSONObject body = new JSONObject()
        .put("refundId", "C7231")
        .put("reference", "B37641")
        .put("amount", 2375);
    
    HttpResponse<JsonNode> response = Unirest.post(baseUrl + "/payments/v1/refunds")
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer myOAuthToken")
        .body(body)
        .asJson();
    
    const refundRequest = {
        refundId: "C7231",
        reference: "B37641",
        amount: 2375
    }
    
    const response = fetch(baseUrl + '/payments/v1/refunds', {
        method: 'post',
        credentials: 'omit',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': 'Bearer myOAuthToken'
        },
        body: JSON.stringify(refundRequest)
    })
    

    Make sure to replace myOAuthToken with the OAuth token retrieved from the /oauth/token endpoint.

    Request

    POST /payments/v1/refunds

    Request Body Parameters

    Parameter Type Required Description
    refundId String Yes A unique identifier for the refund request. If a request is received with a refundId that has already been processed, it will be ignored.
    amount Integer Yes The refund amount, in cents
    reference String Yes The reference that was used to create the original purchase transaction which is now being refunded.

    Returns

    HTTP status 200 Ok is returned upon success. There is no JSON body.

    Errors

    Errors related to a missing or invalid OAuth token will return a 401 Unauthorized.

    Errors due to an invalid request will return a 422 Unprocessable Entity, with possible error codes 1016, 1049, 1800, and 1801.

    See Errors for details.

    Webhook

    When a transaction concludes (i.e. is completed, or declined by the consumer, or fails), the Payments Gateway will call the webhook that is configured. The webhook is called with a POST operation, and passed a JSON Web Token (JWT) as a path variable. It is critical to verify the signature of the JWT before consuming the payload.

    Report Transaction Result

    When decoded, the JSON body of the JWT will look like this:

    {
        "reference": "B37641",
        "transactionId": 94824,
        "result": "paid",
        "amount": 2375,
        "fee": 25
    }
    

    Webhook Result Fields

    Field Type Always Present Description
    reference String Yes The reference string provided in the authorization request
    transactionId Integer Yes The ChangeJar transaction ID.
    result String Yes Indicates if the transaction succeeded (“paid”), was declined by the consumer (“declined”), or failed (“failed”)
    amount Integer Yes The originally requested transaction amount in cents, if the transaction succeeded, or zero otherwise. For convenience only; can be ignored.
    fee Integer Yes The user license fee incurred by this transaction, in cents

    Error Codes

    HTTP Responses

    Example 401 body:

    {
        "error": "unauthorized",
        "error_description": "Full authentication is required to access this resource"
    }
    

    Example of 422 body:

    {
        "code": 1800,
        "message": "Specified user is not authorized",
        "developerMessage": "User 'bartsimpson' is not authorized"
    }
    

    The API will return a 401 Unauthorized in the case where the OAuth token is missing, invalid, or expired. In all of these cases, there will be a JSON body explaining the cause of the error. The format of the JSON body in these cases is as follows:

    Field Description
    error A single-word description of the error
    error_description A more verbose explanation of the error

    All other API errors will return a 422 Unprocessable Entity, with a JSON body containing explaining the cause of the error.

    Field Description
    code A numeric code identifying the error
    message A user-presentable error message, in English
    developer_message A more detailed explanation of the error, intended for developers, in Engligh

    The following codes may be returned by the API:

    Code Message Description
    1016 Incorrect re-send of request A request was re-sent with a previously used reference, but one or more parameters of the request changed since it was originally sent.
    1032 Invalid state for that action The request attempted to modify a payment in a way that is not permitted given the payment's state (for example, trying to cancel a payment that is already completed)
    1049 Request validation failed A mandatory field in the input was missing, or a field had an illegal value.
    1800 Specified user is not authorized The supplied OAuth token was issued to a user who is not authorized to access the Payment API.
    1801 No such payment request A request was issued using a reference code that is not valid.