diff --git a/lib/partner.js b/lib/partner.js
index c20b374..47c16ad 100644
--- a/lib/partner.js
+++ b/lib/partner.js
@@ -34,6 +34,10 @@ function createXmlRpcClient(endpoint) {
 }
 
 function buy(task) {
+    _topUpRequest(task);
+}
+
+function _topUpRequest(task, isAdvice) {
     const params = {
         MSISDN: config.partner.msisdn || config.partner.userid,
         REQUESTID: task.trx_id,
@@ -54,10 +58,13 @@ function buy(task) {
             let rc = '68';
 
             if (
-                err.code === 'ECONNREFUSED'
-                || err.code === 'EHOSTUNREACH'
-                || (err.code === 'ETIMEDOUT' && err.syscall === "connect")
-                || (err.code === 'EPROTO' && err.syscall === "write")
+                !isAdvice &&
+                (
+                    err.code === 'ECONNREFUSED'
+                    || err.code === 'EHOSTUNREACH'
+                    || (err.code === 'ETIMEDOUT' && err.syscall === "connect")
+                    || (err.code === 'EPROTO' && err.syscall === "write")
+                )
             ) {
                 rc = '91';
             }
@@ -99,7 +106,7 @@ function buy(task) {
     });
 }
 
-function advice(task) {
+function _topUpInquiry(task) {
     const params = {
         REQUESTID: task.trx_id,
         MSISDN: config.partner.msisdn || config.partner.userid,
@@ -127,11 +134,6 @@ function advice(task) {
                 }
             });
 
-            setTimeout(
-                function() { advice(task); },
-                60 * 1000
-            );
-
             return;
         }
 
@@ -152,6 +154,19 @@ function advice(task) {
     });
 }
 
+function advice(task) {
+    if (config && advice_is_not_allowed) {
+        return;
+    }
+
+    if (config && advice_is_topuprequest) {
+        _topUpRequest(task, true);
+    }
+    else {
+        _topUpInquiry(task);
+    }
+}
+
 function report(data) {
     if (!data) {
         return;
diff --git a/lib/st24.js b/lib/st24.js
index bd5b311..d0e54f8 100644
--- a/lib/st24.js
+++ b/lib/st24.js
@@ -1,16 +1,32 @@
 "use strict";
 
-function extractSnFromMessage(msg) {
+function extractSnFromMessage(msg, custom_rule) {
     if (!msg || typeof msg !== 'string') {
         return;
     }
 
-    let match = msg.match(/^SN=(.*?);/);
-    if (!match || match.length < 2) {
-        return;
+    let pattern;
+    let pattern_match_idx;
+
+    if (custom_rule && custom_rule.pattern) {
+        pattern = custom_rule.pattern;
+        pattern_match_idx = custom_rule.match_idx;
+    }
+    else {
+        pattern = "^SN=(.*?);";
+        pattern_match_idx = 1;
     }
 
-    return match[1];
+    const re = new RegExp(pattern);
+    const matches = msg.match(re);
+
+    if (!matches) return;
+
+    if (pattern_match_idx < matches.length) {
+        return matches[pattern_match_idx];
+    } else {
+        return;
+    }
 }
 
 function extractPriceFromMsg(msg) {
diff --git a/rc-local.sample.kopnus.json b/rc-local.sample.kopnus.json
new file mode 100644
index 0000000..3664726
--- /dev/null
+++ b/rc-local.sample.kopnus.json
@@ -0,0 +1,11 @@
+{
+    "00": "00",
+    "14": "14",
+    "15": "88",
+    "68": "68",
+    "74": "77",
+    "82": "94",
+    "I8": "14",
+    "I9": "14",
+    "A0": "68"
+}