@dineengine/spendgo-oauth
v1.0.0
Published
Performs OAuth for the Spendgo OAuth APIs
Downloads
34
Readme
@dineengine/spendgo-oauth
Performs OAuth for the Spendgo OAuth APIs
Install
npm install @dineengine/spendgo-oauth
npx cap syncUsage Example
import { DineEngineSpendgoOAuth } from '@dineengine/spendgo-oauth';
// Configure the OAuth client
await DineEngineSpendgoOAuth.configure({
clientId: 'your-client-id',
clientSecret: 'your-client-secret', // See security section below
redirectUri: 'com.yourapp.scheme://oauth',
sandbox: true, // true for sandbox (skuped.com), false for production (spendgo.com)
scopes: ['read', 'write'] // optional, defaults to ['read']
});
// Start OAuth flow
try {
const result = await DineEngineSpendgoOAuth.authorize();
if (result.success) {
console.log('Access Token:', result.tokens.access_token);
console.log('Refresh Token:', result.tokens.refresh_token);
console.log('Expires at:', new Date(result.tokens.access_token_expires_on));
} else {
console.error('OAuth failed:', result.error);
}
} catch (error) {
console.error('OAuth error:', error);
}
// Check token status
const status = await DineEngineSpendgoOAuth.getTokenStatus();
console.log('Token is valid:', status.isValid);
// Refresh token when needed
if (!status.isValid) {
const refreshResult = await DineEngineSpendgoOAuth.refreshToken();
if (refreshResult.success) {
console.log('Token refreshed successfully');
}
}
// Get current access token
const { token } = await DineEngineSpendgoOAuth.getAccessToken();
console.log('Current access token:', token);
// Logout and clear tokens
await DineEngineSpendgoOAuth.logout();Security: Client Secret Storage
⚠️ IMPORTANT: Never hardcode client secrets in your application code. Client secrets must be stored securely and injected at build time.
Recommended Approach: Environment Variables
iOS Setup (Info.plist)
- Add your client secret to your iOS
Info.plist:
<key>SpendgoClientSecret</key>
<string>$(SPENDGO_CLIENT_SECRET)</string>- Set the environment variable in your Xcode build settings or CI/CD:
export SPENDGO_CLIENT_SECRET="your-actual-client-secret"- Read the value in your app:
import { NativeSettings } from '@capacitor/core';
const clientSecret = await NativeSettings.get({ key: 'SpendgoClientSecret' });
// Or use Capacitor's Preferences pluginAndroid Setup (BuildConfig)
- Add to your
android/app/build.gradle:
android {
defaultConfig {
buildConfigField "String", "SPENDGO_CLIENT_SECRET", "\"${System.getenv('SPENDGO_CLIENT_SECRET') ?: 'default-dev-secret'}\""
}
}- Access in your app via a native method or create a helper:
public class ConfigHelper {
public static String getClientSecret() {
return BuildConfig.SPENDGO_CLIENT_SECRET;
}
}Alternative: Secure Storage Plugin
For runtime configuration, use a secure storage plugin:
npm install @capacitor-community/secure-storageimport { SecureStoragePlugin } from '@capacitor-community/secure-storage';
// Store securely (one-time setup)
await SecureStoragePlugin.set({
key: 'spendgo_client_secret',
value: 'your-client-secret'
});
// Retrieve for OAuth configuration
const { value: clientSecret } = await SecureStoragePlugin.get({
key: 'spendgo_client_secret'
});What NOT to do:
❌ Never do this:
// DON'T hardcode secrets in source code
const clientSecret = 'sk_live_1234567890abcdef'; // This will be visible in your app bundle❌ Never store in:
- Plain text files
- JavaScript constants
- Git repositories
- Client-side configuration files
Configuration
iOS Setup
Add the following to your Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>OAuth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.yourapp.scheme</string>
</array>
</dict>
</array>Android Setup
- Add OAuth redirect scheme to your
AndroidManifest.xml:
Add an intent filter to your main activity to handle OAuth redirects:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<!-- Your existing intent filters -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- OAuth redirect handler -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.yourapp.scheme" />
</intent-filter>
</activity>- Configure manifest placeholders in
android/app/build.gradle:
android {
defaultConfig {
applicationId "com.yourapp.package"
// ... other config
// OAuth redirect scheme configuration
manifestPlaceholders = [
appAuthRedirectScheme: 'com.yourapp.scheme'
]
}
}Note: Replace com.yourapp.scheme with your actual app's package name or a custom scheme (e.g., com.mycompany.myapp).
MainActivity Setup (Required for Android)
For OAuth redirects to work properly on Android, you need to modify your MainActivity.java to handle OAuth redirect intents. This is required because the OAuth flow returns to your app via a deep link, and the MainActivity needs to pass this intent to the OAuth plugin.
Update your MainActivity.java:
package com.yourapp.package; // Replace with your actual package name
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.PluginHandle;
import com.dineengine.plugins.spendgooauth.DineEngineSpendgoOAuthPlugin;
public class MainActivity extends BridgeActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MainActivity onCreate called");
// Handle OAuth redirect if the app was launched with an intent
handleOAuthRedirect(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "MainActivity onNewIntent called");
// Handle OAuth redirect when the app is already running
handleOAuthRedirect(intent);
}
private void handleOAuthRedirect(Intent intent) {
if (intent == null) {
Log.d(TAG, "Intent is null, skipping OAuth redirect handling");
return;
}
Uri data = intent.getData();
Log.d(TAG, "Intent data: " + (data != null ? data.toString() : "null"));
if (data != null && "com.yourapp.scheme".equals(data.getScheme())) {
Log.d(TAG, "OAuth redirect detected, passing to plugin");
// This is an OAuth redirect, pass it to the OAuth plugin
PluginHandle pluginHandle = getBridge().getPlugin("DineEngineSpendgoOAuth");
if (pluginHandle != null && pluginHandle.getInstance() instanceof DineEngineSpendgoOAuthPlugin) {
DineEngineSpendgoOAuthPlugin oauthPlugin = (DineEngineSpendgoOAuthPlugin) pluginHandle.getInstance();
oauthPlugin.handleOAuthRedirect(intent);
Log.d(TAG, "OAuth redirect passed to plugin successfully");
} else {
Log.e(TAG, "OAuth plugin not found or wrong type");
}
} else {
Log.d(TAG, "Not an OAuth redirect, ignoring");
}
}
}Important Notes:
- Replace
com.yourapp.packagewith your actual package name (should match yourapplicationIdinbuild.gradle) - Replace
com.yourapp.schemewith the same scheme you used in yourAndroidManifest.xmlandredirectUriconfiguration - Ensure your
AndroidManifest.xmlhasandroid:launchMode="singleTask"for the MainActivity (this is usually set by default in Capacitor apps)
Why This is Required:
The OAuth flow works as follows:
- Your app opens the OAuth provider in a Custom Tab
- User completes authentication
- OAuth provider redirects to your app using the configured scheme (e.g.,
com.yourapp.scheme://oauth) - Android opens your app with this deep link intent
- Your MainActivity receives the intent and must pass it to the OAuth plugin
- The plugin processes the authorization code and exchanges it for tokens
Without this MainActivity setup, the OAuth redirect will return to your app but the plugin won't receive the authorization response, causing the OAuth flow to hang.
Environment Configuration
Sandbox vs Production
The plugin supports two environments:
- Sandbox (
sandbox: true): Usesskuped.comfor testing - Production (
sandbox: false): Usesspendgo.comfor live transactions
Configure based on your build environment:
const isProduction = process.env.NODE_ENV === 'production';
await DineEngineSpendgoOAuth.configure({
clientId: 'your-client-id',
clientSecret: getClientSecret(), // Use secure method above
redirectUri: 'com.yourapp.scheme://oauth',
sandbox: !isProduction, // true for development, false for production
scopes: ['read', 'write']
});SpendGo API Integration
This plugin provides OAuth authentication for the SpendGo API. Once authenticated, you can use the access token to make API calls to SpendGo's services:
- Member Profile:
GET /member/profile - Member Balance:
GET /member/balance - Member Rewards:
GET /member/rewards - And more...
Example API Call
const { token } = await DineEngineSpendgoOAuth.getAccessToken();
const response = await fetch('https://identity.spendgo.com/v3/member/profile', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const memberData = await response.json();See the SpendGo API Documentation for complete API reference.
API
configure(...)authorize()refreshToken()getTokenStatus()logout()getAccessToken()clearTokens()- Interfaces
configure(...)
configure(config: SpendgoOAuthConfig) => Promise<void>Configure the OAuth client with SpendGo credentials
| Param | Type |
| ------------ | ----------------------------------------------------------------- |
| config | SpendgoOAuthConfig |
authorize()
authorize() => Promise<SpendgoAuthResult>Start the OAuth authorization flow
Returns: Promise<SpendgoAuthResult>
refreshToken()
refreshToken() => Promise<SpendgoRefreshResult>Refresh the access token using the refresh token
Returns: Promise<SpendgoRefreshResult>
getTokenStatus()
getTokenStatus() => Promise<SpendgoTokenStatus>Get the current token status
Returns: Promise<SpendgoTokenStatus>
logout()
logout() => Promise<SpendgoLogoutResult>Logout and clear stored tokens
Returns: Promise<SpendgoLogoutResult>
getAccessToken()
getAccessToken() => Promise<{ token?: string; }>Get the stored access token
Returns: Promise<{ token?: string; }>
clearTokens()
clearTokens() => Promise<void>Clear all stored tokens and configuration
Interfaces
SpendgoOAuthConfig
| Prop | Type |
| ------------------ | --------------------- |
| clientId | string |
| clientSecret | string |
| redirectUri | string |
| sandbox | boolean |
| scopes | string[] |
SpendgoAuthResult
| Prop | Type |
| ------------- | --------------------------------------------------------------------- |
| success | boolean |
| tokens | SpendgoTokenResponse |
| error | string |
SpendgoTokenResponse
| Prop | Type |
| ----------------------------------- | ------------------- |
| token_type | string |
| scope | string |
| access_token | string |
| access_token_expires_in_secs | number |
| access_token_expires_on | number |
| refresh_token | string |
| refresh_token_expires_in_secs | number |
| refresh_token_expires_on | number |
SpendgoRefreshResult
| Prop | Type |
| ------------- | --------------------------------------------------------------------- |
| success | boolean |
| tokens | SpendgoTokenResponse |
| error | string |
SpendgoTokenStatus
| Prop | Type |
| ---------------------- | -------------------- |
| isValid | boolean |
| accessToken | string |
| refreshToken | string |
| expiresAt | number |
| refreshExpiresAt | number |
SpendgoLogoutResult
| Prop | Type |
| ------------- | -------------------- |
| success | boolean |
| error | string |
