SunLync API V2

Processing Card Transactions

Overview

The API can charge the credit card already stored against a client's membership, without the card number ever being handled by your application. The stored card is identified by a token held in SunLync, and only that token is sent to the payment processor.

A complete transaction is three calls, in order: the sale is recorded, the card is charged, and the payment is recorded against the sale. The first call is what produces the receipt number that ties the other two to it.

Before you start

A charge will only be accepted when all of the following are true.

If any of these is not met the request is rejected before any attempt is made to charge the card. Nothing is sent to the processor and nothing is charged.

Processing a transaction

  1. Post the transaction records. Call POST /slapi/clients/inserttransaction with the client, store, employee and a lineItems array describing what is being sold. It responds 201 with a receiptNo. This is the receipt number for the whole sale, and both remaining calls need it, so do this first.
  2. Charge the stored card. Call POST /slapi/clients/slimcdchargecardonfile with the client, membership, store and amount, passing the receiptNo from step 1 as receiptNumber. The response tells you whether the card was approved and, if so, gives you the authorisation code.
  3. Post the payment record. If and only if the charge was approved, call POST /slapi/clients/insertpayment with the same receiptNumber to record how the sale was paid for.
Neither the sale nor the payment is recorded automatically. An approved charge takes the customer's money and writes an entry to the card processing log. It does not create the sale, the receipt, the payment or any ledger entry. If your application charges the card without completing steps 1 and 3, the money will have been taken with no financial record of it in SunLync.

Line items

Each object in lineItems needs a tranType, a description, pricePerUnit, units, amount, and tax1, tax2 and tax3. The type decides which one further field is required.

tranTypeSellsAlso requires
M or 1A membershipmembershipId
PA packagepackageId
RA retail productproductNumber
GA gift certificategiftCertNo
The amount you charge in step 2 is whatever the customer is paying, and does not have to be the whole sale. Splitting a sale across two cards, or part cash and part card, simply means two or more charges against the same receipt number.

Recording the payment

insertpayment needs the receiptNumber, the clientNumber, storecode, employeeNumber, amount, a promoNumber, and a paymentNumber identifying how it was paid. The payment numbers configured for the database are returned by GET /slapi/setup/paymenttypes, so read them once and use the one that corresponds to a credit card payment.

Charging the card

Request

FieldDescription
clientNumber Required The client whose stored card is to be charged.
membershipId Required The active EFT membership the stored card belongs to.
storecode Required The store the transaction is taken at. Its processor credentials are used.
amount Required The amount to charge. Must be greater than 0.00.
chargeAttemptId Required A value you generate that identifies this charge attempt. Up to 64 characters; a GUID is the usual choice. See Retrying safely below, which is the whole reason this field exists.
receiptNumber Optional The receiptNo returned by inserttransaction. Passed to the processor as the transaction reference and stored on the log entry. Supplying it is strongly recommended: without it there is nothing tying the charge to the sale it paid for.
employeeNumber Optional The employee taking the transaction, recorded on the log entry.
{
  "clientNumber": "0000103018",
  "membershipId": "0000004821",
  "storecode": "0000000039",
  "amount": "19.58",
  "receiptNumber": "0018601941",
  "employeeNumber": "0000007817",
  "chargeAttemptId": "9f2c1ab4-7d55-4e1a-9c80-6b3f0e2d41aa"
}

Response

FieldDescription
approvedtrue when the card was approved, false when it was refused.
authCodeThe authorisation code, when approved.
referenceThe processor's reference for the transaction.
responseCodeThe processor's response code.
descriptionThe processor's description of the result.
logIdThe card processing log entry for this attempt. Quote it when querying a transaction.
amountThe amount that was submitted.
maskedAcctNoThe masked stored card number, so a receipt can show which card was used.
nameOnAccountThe name held against the stored card.
deductTypeThe card type of the stored card.
replayedfalse for a charge that was actually made, true when a repeated chargeAttemptId returned an earlier result instead of charging again.
warningPresent only when the charge completed but its log entry could not be updated. See below.
{
  "approved": true,
  "authCode": "008093",
  "reference": "7013997703",
  "responseCode": "0",
  "description": "APPROVAL",
  "logId": 2303358,
  "amount": "19.58",
  "maskedAcctNo": "XXXXXXXXXXXX9014",
  "nameOnAccount": "Sample Cardholder",
  "deductType": "Visa",
  "replayed": false,
  "message": ""
}

Reading the outcome

There are three outcomes, and they need to be handled differently.

Approved

HTTP 200 with approved set to true. The card has been charged. Post the payment record, as in step 3.

Refused

HTTP 200 with approved set to false. The card was declined, or the processor rejected the transaction. Nothing has been charged and no payment should be posted. description carries the reason.

A refusal is a complete answer to the request, so it is reported as a success at the HTTP level. Check approved rather than the status code to decide whether money moved.

Unknown

HTTP 502 with chargeStateKnown set to false. The request reached the processor but no answer came back, so whether the card was charged is genuinely unknown. This is rare, and usually means a network fault or a timeout.

Retry this by sending the request again with the same chargeAttemptId, as described below. Do not generate a new key for a retry: that is what turns one sale into two charges.

Retrying safely

Generate the chargeAttemptId once, before the first attempt, and send that same key on every retry of that charge. The API records the key against the attempt, so a repeated key is recognised as a retry rather than a new sale.

What happens on a repeat depends on how the first attempt ended.

First attemptRepeat returns
Finished, approved or refused 200 with the original result and replayed set to true. The card is not charged again.
Outcome never came back 409. The API will not charge, because doing so could be the second charge for one sale. Establish what happened to that attempt, using the logId, before charging again.
Generate the key once per charge, not once per request. Creating a new key inside your retry loop makes every attempt look like a new sale and removes the protection entirely, while appearing to work normally.
A refused charge stays refused under the same key. The key identifies the attempt, not the intention, so repeating a key that was declined or rejected returns that same outcome rather than trying again. Once you have corrected whatever caused it — the amount, the card on file — that is a fresh attempt and needs a fresh key.

A deliberate second charge is a different sale and needs a new key. Two charges against one receipt number, for a split payment, are two charges with two keys.

If a retry may happen after your application restarts, keep the key somewhere it survives that, alongside the pending sale rather than in memory.

When the charge succeeds but the log entry does not update

Every attempt is written to the card processing log before the card is charged, and updated with the result afterwards. If that update fails the response still reports the charge normally and adds a warning field.

Treat this as an approved charge: the money has moved and the payment should be posted as usual. The log entry will show the attempt without its result, so pass the authCode and reference on to whoever reconciles the day's transactions.

Rejections

These are returned as HTTP 400 before any attempt is made to charge the card, in the error field.

Message ends withMeaning
Invalid Client NumberNo such client.
Client is not activeThe client exists but is not active.
Invalid Membership IdNo such membership.
Membership is not activeThe membership is not in an active state.
Membership does not belong to clientThe membership and client do not match.
Invalid storecodeNo such store.
Card on account transactions are not enabledThe store is not configured for card-on-account.
Customer has no billable EFT Token on file to chargeNo stored card is held for the membership.
Customer has no unexpired EFT Token on file to chargeA card is stored but its expiry date has passed.
invalid amountThe amount could not be read as a number.
amount must be greater than 0.00A zero or negative amount was supplied.
the database needs to be updatedThe database is not yet at a version that supports this endpoint. Apply the latest SunLync database update.

A 500 is returned if the store's processor credentials cannot be read, or if the attempt could not be written to the card processing log. In both cases nothing has been charged.