An unofficial SDK for RDCW Slip Verify with a clean factory function API combining functional and OOP paradigms.
npm install node-rdcw-slipverifyimport { createRdcwVerify } from "node-rdcw-slipverify";
const rdcw = createRdcwVerify({
clientId: "your-client-id",
secret: "your-client-secret",
});
// Verify a slip from payload
const result = await rdcw.inquiryPayload(
"0038000600000101030060217Bf870bf26685f55526203TH9104CF62"
);
if (result.error) {
console.log("Error:", result.error.message);
} else {
console.log("Success:", result.data);
}The SDK supports multiple locales for error messages. By default, it uses English (en), but you can configure it to use Thai (th) or provide custom messages.
import { createRdcwVerify } from "node-rdcw-slipverify";
const rdcw = createRdcwVerify({
clientId: "your-client-id",
secret: "your-client-secret",
locale: "th", // Use Thai locale
});
// All error messages will now be in Thai
const result = await rdcw.inquiryPayload(invalidPayload);
if (result.error) {
console.log(result.error.message); // "สลิปไม่ถูกต้อง" instead of "Invalid slip"
}You can also provide custom messages to override the default locale messages:
const rdcw = createRdcwVerify({
clientId: "your-client-id",
secret: "your-client-secret",
locale: "en",
customMessages: {
validation: {
invalidSlip: "The slip you provided is not valid",
slipExpired: "This slip is too old to be processed",
},
qr: {
notFound: "We couldn't find a QR code in your image",
},
},
});en- English (default)th- Thai (ภาษาไทย)
import { createRdcwVerify } from "node-rdcw-slipverify";
const rdcw = createRdcwVerify({
clientId: "your-client-id",
secret: "your-client-secret",
});
// Verify from payload
const result = await rdcw.inquiryPayload(payload);
// Verify from image
const imageResult = await rdcw.inquiryImage(imageBuffer);
if (result.data) {
console.log("Transaction details:", result.data);
}const result = await rdcw.inquiryPayload(payload, {
expectedAccount: "1234567890",
expectedBank: "014",
expectedAmount: "100.00", // Optional
});
if (result.error) {
console.log("Validation failed:", result.error.message);
// Handle specific error types
switch (result.error.type) {
case "INVALID_SLIP":
// Handle invalid slip
break;
case "EXPIRED_SLIP":
// Handle expired slip
break;
case "VALIDATION_ERROR":
// Handle validation errors
break;
case "API_ERROR":
// Handle API errors
break;
case "QR_CODE_ERROR":
// Handle QR code errors
break;
}
} else {
console.log("Validation successful!", result.data);
}const result = await rdcw.inquiryImage(imageBuffer, {
expectedAccount: "1234567890",
expectedBank: "014",
onSuccess: (data) => {
console.log("✅ Verification successful!");
console.log("Amount:", data.data.amount);
console.log("From:", data.data.sender.displayName);
},
onError: (error) => {
console.log("❌ API Error:", error.message);
},
onValidationError: (error) => {
console.log("⚠️ Validation failed:", error.message);
},
});
// Result is still returned for further processing
if (result.data) {
// Save to database, etc.
}// First, verify the slip
const verifyResult = await rdcw.inquiryPayload(payload);
if (verifyResult.data) {
// Then, validate manually
const validateResult = rdcw.validate(verifyResult.data, {
expectedAccount: "1234567890",
expectedBank: "014",
expectedAmount: "100.00",
onSuccess: (data) => console.log("Validation passed!"),
onValidationError: (error) =>
console.log("Validation failed:", error.message),
});
if (validateResult.data) {
console.log("All checks passed!");
}
}import fs from "fs";
// From file buffer
const imageBuffer = fs.readFileSync("path/to/qr-code-image.png");
const result = await rdcw.inquiryImage(imageBuffer, {
expectedAccount: "1234567890",
expectedBank: "014",
});
// From base64 string
const base64Image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...";
const result2 = await rdcw.inquiryImage(base64Image);Creates a new RDCW Verify instance.
Parameters:
config.clientId(string, required): Your SlipVerify client IDconfig.secret(string, required): Your SlipVerify client secretconfig.baseUrl(string, optional): Custom API base URL (default: "https://suba.rdcw.co.th")config.locale(Locale, optional): Locale for error messages -"en"or"th"(default: "en")config.customMessages(Partial, optional): Custom message overrides
Returns: RdcwVerify instance
Verifies a slip using its payload string.
Parameters:
payload(string): The QR code payload stringoptions(ValidationOptions, optional): Validation options
Returns: Promise<Result<VerifySlipResult, SlipError>>
Reads a QR code from an image and verifies the slip.
Parameters:
imageData(ArrayBuffer | Buffer | string): Image data (buffer or base64 string)options(ValidationOptions, optional): Validation options
Returns: Promise<Result<VerifySlipResult, SlipError>>
Manually validates a verification result.
Parameters:
result(VerifySlipResult): The result from inquiryoptions(ValidationOptions): Validation options
Returns: Result<VerifySlipResult, SlipError>
type Locale = "en" | "th";interface LocaleMessages {
qr: {
invalidDimensions: string;
notFound: string;
readFailed: string;
};
api: {
invalidResponse: string;
requestFailed: string;
unexpectedError: string;
};
validation: {
invalidSlip: string;
slipAlreadyUsed: string;
slipExpired: string;
invalidAccount: string;
invalidBank: string;
invalidQRFormat: string;
amountMismatch: string;
};
}type Result<T, E> = Success<T> | Failure<E>;
interface Success<T> {
data: T;
error?: never;
}
interface Failure<E> {
data?: never;
error: E;
}interface ValidationOptions {
expectedAccount?: string;
expectedBank?: string;
expectedAmount?: string;
onSuccess?: (data: VerifySlipResult) => void;
onError?: (error: SlipError) => void;
onValidationError?: (error: SlipError) => void;
}interface SlipError {
type: ErrorType;
message: string;
}
type ErrorType =
| "INVALID_SLIP"
| "EXPIRED_SLIP"
| "QR_CODE_ERROR"
| "API_ERROR"
| "VALIDATION_ERROR";interface VerifySlipResult {
discriminator: string;
valid: boolean;
data: Data;
quota: Quota;
subscription: Subscription;
isCached: boolean;
}
interface Data {
language: string;
transRef: string;
sendingBank: string;
receivingBank: string;
transDate: string;
transTime: string;
sender: Receiver;
receiver: Receiver;
amount: string;
paidLocalAmount: string;
paidLocalCurrency: string;
countryCode: string;
transFeeAmount: string;
ref1: string;
ref2: string;
ref3: string;
toMerchantId: string;
}The SDK validates slips based on the following criteria:
- Validity: The slip must be marked as valid by the API
- Cache Status: The slip must not have been used before (not cached)
- Age: The slip must not be older than 1 day
- Account Number: Matches the expected account (if provided)
- Bank Code: Matches the expected bank (if provided)
- Amount: Matches the expected amount (if provided)
All methods return a Result type that contains either data or error:
const result = await rdcw.inquiryPayload(payload);
if (result.error) {
// Handle error
console.error(result.error.type, result.error.message);
} else {
// Process data
console.log(result.data);
}INVALID_SLIP: The slip is invalidEXPIRED_SLIP: The slip is older than 1 dayQR_CODE_ERROR: Failed to read or parse QR codeAPI_ERROR: API request failedVALIDATION_ERROR: Validation checks failed
# Install dependencies
npm install
# Build the package
npm run build
# Run in development mode
npm run devnpm run build- Build the packagenpm run dev- Run the package in development modenpm run clean- Remove build artifactsnpm run prepare- Prepare the package for publishing
This project uses semantic-release with the Angular Commit Message Convention for automated releases.
Format:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
Types:
feat: A new feature (triggers minor release)fix: A bug fix (triggers patch release)docs: Documentation changesstyle: Code style changes (formatting, missing semi colons, etc)refactor: Code refactoringperf: Performance improvements (triggers patch release)test: Adding or updating testschore: Maintenance tasksci: CI/CD changes
Breaking Changes:
Add BREAKING CHANGE: in the commit footer to trigger a major release:
feat(api): change validation API
BREAKING CHANGE: The validate method now returns a Result type instead of throwing errors
Examples:
# Patch release (1.0.0 -> 1.0.1)
fix(validation): correct bank code validation
# Minor release (1.0.0 -> 1.1.0)
feat(locale): add Thai language support
# Major release (1.0.0 -> 2.0.0)
feat(api): redesign SDK API
BREAKING CHANGE: Removed deprecated methods and changed initialization patternAutomated Release Process:
- Push commits to
mainbranch following the commit convention - GitHub Actions automatically runs semantic-release
- Version is bumped based on commit types
- CHANGELOG.md is generated
- Package is published to npm with provenance
- GitHub Release is created with release notes
No manual version bumping or tagging needed! 🎉
ISC