규모확장 Wave B: 전 11모듈 svc 75→133·batch 35→46 (BAIS 온라인 1:1)

- svc 825→1,463 (모듈당 133), batch 385→506 (모듈당 46) — 전부 새 고유 실업무 로직
  + 카피북 헤더, 디스패처 133 advertise, 한글 기능설명(매니페스트 추출)
- BAIS As-Is 대비: 온라인 1,463 vs 1,466(99.8%), 배치 506 vs 503(초과)
- 화면 매니페스트 1,463 재생성
- 검증: build DONE modules=11 batches=506, 12서버 runok, xadmin psc 1,466 AVAIL,
  매입체인 XA 커밋, 신규서비스 실데이터 스모크, 전 코퍼스 .pgc 클론 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hyeongwoo-choi 2026-07-20 22:55:47 +00:00
parent ea90bef264
commit c62d91b7a7
1526 changed files with 59213 additions and 12 deletions

View file

@ -94,6 +94,65 @@
#include "RISK_SCORE_INQ.h"
#include "APPROVAL_RATE.h"
#include "AUTH_DUP_CHK.h"
/* --- scale-out services (승인/한도 심화) --- */
#include "LIMIT_MONTH_CHK.h"
#include "LIMIT_MONTH_INQ.h"
#include "LIMIT_PERTX_CHK.h"
#include "LIMIT_CUMUL_INQ.h"
#include "LIMIT_DAILY_INQ.h"
#include "LIMIT_ADJUST.h"
#include "LIMIT_FREEZE.h"
#include "LIMIT_UNFREEZE.h"
#include "LIMIT_TIER_INQ.h"
#include "LIMIT_OVER_ALERT.h"
#include "CARD_LOST_REG.h"
#include "CARD_STOLEN_REG.h"
#include "CARD_REACTIVATE.h"
#include "CARD_STATE_HIST.h"
#include "CARD_EXPIRE_CHK.h"
#include "CARD_ACTIVATE.h"
#include "CHANNEL_POLICY.h"
#include "BRAND_POLICY.h"
#include "MCC_POLICY.h"
#include "CHANNEL_STAT.h"
#include "BRAND_AUTH_STAT.h"
#include "MCC_AUTH_STAT.h"
#include "AUTH_PARTAPPR.h"
#include "PREAUTH_PARTUSE.h"
#include "CANCEL_AMT_INQ.h"
#include "CANCEL_FULL.h"
#include "CANCEL_HISTORY.h"
#include "CANCEL_SUMMARY.h"
#include "VELOCITY_DAY.h"
#include "VELOCITY_AMT.h"
#include "VELOCITY_MCC.h"
#include "VELOCITY_CHAN.h"
#include "VELOCITY_RESET.h"
#include "FRAUD_RULE_NIGHT.h"
#include "FRAUD_RULE_INTL.h"
#include "FRAUD_RULE_MCC.h"
#include "FRAUD_SCORE_SUM.h"
#include "FRAUD_CASE_CLOSE.h"
#include "FRAUD_CASE_LIST.h"
#include "WHITELIST_ADD.h"
#include "WHITELIST_DEL.h"
#include "WHITELIST_CHK.h"
#include "BLACKLIST_COUNT.h"
#include "BLACKLIST_PROMO.h"
#include "TOKEN_ISSUE.h"
#include "TOKEN_REVOKE.h"
#include "TOKEN_INQ.h"
#include "KEY_ROTATE_LOG.h"
#include "STANDIN_STAT.h"
#include "STANDIN_RECON.h"
#include "STANDIN_LIMIT.h"
#include "STAT_DECLINE.h"
#include "STAT_DAILY_SUM.h"
#include "STAT_AMT_BAND.h"
#include "STAT_APPROVE_TR.h"
#include "AUTH_REPROC_REQ.h"
#include "AUTH_REPROC_STAT.h"
#include "LIMIT_EXHAUST_LST.h"
/* ----- advertise table: {service name, function} (fn is extern from its file) */
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
@ -135,6 +194,36 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
{"MERCH_AUTH_RANK", MERCH_AUTH_RANK}, {"CARD_DAILY_RPT", CARD_DAILY_RPT},
{"RISK_SCORE_INQ", RISK_SCORE_INQ}, {"APPROVAL_RATE", APPROVAL_RATE},
{"AUTH_DUP_CHK", AUTH_DUP_CHK},
/* --- scale-out services (승인/한도 심화) --- */
{"LIMIT_MONTH_CHK", LIMIT_MONTH_CHK}, {"LIMIT_MONTH_INQ", LIMIT_MONTH_INQ},
{"LIMIT_PERTX_CHK", LIMIT_PERTX_CHK}, {"LIMIT_CUMUL_INQ", LIMIT_CUMUL_INQ},
{"LIMIT_DAILY_INQ", LIMIT_DAILY_INQ}, {"LIMIT_ADJUST", LIMIT_ADJUST},
{"LIMIT_FREEZE", LIMIT_FREEZE}, {"LIMIT_UNFREEZE", LIMIT_UNFREEZE},
{"LIMIT_TIER_INQ", LIMIT_TIER_INQ}, {"LIMIT_OVER_ALERT", LIMIT_OVER_ALERT},
{"CARD_LOST_REG", CARD_LOST_REG}, {"CARD_STOLEN_REG", CARD_STOLEN_REG},
{"CARD_REACTIVATE", CARD_REACTIVATE}, {"CARD_STATE_HIST", CARD_STATE_HIST},
{"CARD_EXPIRE_CHK", CARD_EXPIRE_CHK}, {"CARD_ACTIVATE", CARD_ACTIVATE},
{"CHANNEL_POLICY", CHANNEL_POLICY}, {"BRAND_POLICY", BRAND_POLICY},
{"MCC_POLICY", MCC_POLICY}, {"CHANNEL_STAT", CHANNEL_STAT},
{"BRAND_AUTH_STAT", BRAND_AUTH_STAT}, {"MCC_AUTH_STAT", MCC_AUTH_STAT},
{"AUTH_PARTAPPR", AUTH_PARTAPPR}, {"PREAUTH_PARTUSE", PREAUTH_PARTUSE},
{"CANCEL_AMT_INQ", CANCEL_AMT_INQ}, {"CANCEL_FULL", CANCEL_FULL},
{"CANCEL_HISTORY", CANCEL_HISTORY}, {"CANCEL_SUMMARY", CANCEL_SUMMARY},
{"VELOCITY_DAY", VELOCITY_DAY}, {"VELOCITY_AMT", VELOCITY_AMT},
{"VELOCITY_MCC", VELOCITY_MCC}, {"VELOCITY_CHAN", VELOCITY_CHAN},
{"VELOCITY_RESET", VELOCITY_RESET}, {"FRAUD_RULE_NIGHT", FRAUD_RULE_NIGHT},
{"FRAUD_RULE_INTL", FRAUD_RULE_INTL}, {"FRAUD_RULE_MCC", FRAUD_RULE_MCC},
{"FRAUD_SCORE_SUM", FRAUD_SCORE_SUM}, {"FRAUD_CASE_CLOSE", FRAUD_CASE_CLOSE},
{"FRAUD_CASE_LIST", FRAUD_CASE_LIST}, {"WHITELIST_ADD", WHITELIST_ADD},
{"WHITELIST_DEL", WHITELIST_DEL}, {"WHITELIST_CHK", WHITELIST_CHK},
{"BLACKLIST_COUNT", BLACKLIST_COUNT}, {"BLACKLIST_PROMO", BLACKLIST_PROMO},
{"TOKEN_ISSUE", TOKEN_ISSUE}, {"TOKEN_REVOKE", TOKEN_REVOKE},
{"TOKEN_INQ", TOKEN_INQ}, {"KEY_ROTATE_LOG", KEY_ROTATE_LOG},
{"STANDIN_STAT", STANDIN_STAT}, {"STANDIN_RECON", STANDIN_RECON},
{"STANDIN_LIMIT", STANDIN_LIMIT}, {"STAT_DECLINE", STAT_DECLINE},
{"STAT_DAILY_SUM", STAT_DAILY_SUM}, {"STAT_AMT_BAND", STAT_AMT_BAND},
{"STAT_APPROVE_TR", STAT_APPROVE_TR}, {"AUTH_REPROC_REQ", AUTH_REPROC_REQ},
{"AUTH_REPROC_STAT", AUTH_REPROC_STAT}, {"LIMIT_EXHAUST_LST", LIMIT_EXHAUST_LST},
{NULL, NULL}
};

View file

@ -0,0 +1,16 @@
/*
* au_brand_mix_batch.h - au au_brand_mix_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_BRAND_MIX_BATCH_H
#define AU_BRAND_MIX_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_brand_mix_batch_rec_t;
#endif /* AU_BRAND_MIX_BATCH_H */

View file

@ -0,0 +1,54 @@
/*
* au_brand_mix_batch.pgc - au 브랜드별 승인 믹스 집계 배치 (substr GROUP BY 커서 + XA).
*
* Groups the day's authorizations by card brand (leading digit) reporting count
* and amount, and accumulates the top brand by amount, under one global XA
* transaction. Read-only report. Usage: au_brand_mix_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_brand_mix_batch;
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long total = 0, top_amt = 0;
char top_lead[4] = "";
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_lead[4];
long h_cnt, h_amt;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bmx CURSOR FOR
SELECT substr(card_no, 1, 1), count(*), coalesce(sum(amount),0)
FROM au_authorization
WHERE biz_date = :h_bizdate AND status IN ('A','M','S')
GROUP BY substr(card_no, 1, 1) ORDER BY coalesce(sum(amount),0) DESC;
EXEC SQL OPEN bmx;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bmx INTO :h_lead, :h_cnt, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bmx; tpabort(0); return 1; }
total += h_cnt;
if (h_amt > top_amt) { top_amt = h_amt; strncpy(top_lead, h_lead, sizeof(top_lead)-1); }
printf(" brand_lead=%s cnt=%ld amt=%ld\n", h_lead, h_cnt, h_amt);
}
EXEC SQL CLOSE bmx;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_brand_mix_batch COMMIT: bizdate=%s total=%ld top_lead=%s top_amt=%ld\n",
bizdate, total, top_lead, top_amt);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_card_dormant_batch.h - au au_card_dormant_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_CARD_DORMANT_BATCH_H
#define AU_CARD_DORMANT_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_card_dormant_batch_rec_t;
#endif /* AU_CARD_DORMANT_BATCH_H */

View file

@ -0,0 +1,43 @@
/*
* au_card_dormant_batch.pgc - au 휴면 카드 일괄 표시 배치 (집합 UPDATE + XA).
*
* Counts, then set-updates every ACTIVE card with no authorization in the last
* 90 days to status DORMANT in a single UPDATE, under one global XA transaction.
* No cursor, no dbio. Usage: au_card_dormant_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_card_dormant_batch;
int main(int argc, char **argv)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_cand = 0;
EXEC SQL END DECLARE SECTION;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*) INTO :h_cand FROM card_limit
WHERE status = 'ACTIVE'
AND card_no NOT IN (SELECT DISTINCT card_no FROM au_authorization
WHERE created_at >= now() - interval '90 days');
if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
EXEC SQL UPDATE card_limit SET status = 'DORMANT', updated_at = now()
WHERE status = 'ACTIVE'
AND card_no NOT IN (SELECT DISTINCT card_no FROM au_authorization
WHERE created_at >= now() - interval '90 days');
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_card_dormant_batch COMMIT: dormant_marked=%ld\n", h_cand);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_channel_stat_batch.h - au au_channel_stat_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_CHANNEL_STAT_BATCH_H
#define AU_CHANNEL_STAT_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_channel_stat_batch_rec_t;
#endif /* AU_CHANNEL_STAT_BATCH_H */

View file

@ -0,0 +1,52 @@
/*
* au_channel_stat_batch.pgc - au 채널별 매입 집계 배치 (GROUP BY 커서 + XA).
*
* Reports per-channel count / sum / average purchase amount for a business day,
* scanning a GROUP BY cursor under one global XA transaction. Read-only report.
* Usage: au_channel_stat_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_channel_stat_batch;
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long chans = 0, total = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_chan[16];
long h_cnt, h_sum, h_avg;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE cstb CURSOR FOR
SELECT channel, count(*), coalesce(sum(amount),0),
CAST(coalesce(avg(amount),0) AS bigint)
FROM purchase WHERE biz_date = :h_bizdate
GROUP BY channel ORDER BY count(*) DESC;
EXEC SQL OPEN cstb;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH cstb INTO :h_chan, :h_cnt, :h_sum, :h_avg;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE cstb; tpabort(0); return 1; }
chans++;
total += h_cnt;
printf(" channel=%s cnt=%ld sum=%ld avg=%ld\n", h_chan, h_cnt, h_sum, h_avg);
}
EXEC SQL CLOSE cstb;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_channel_stat_batch COMMIT: bizdate=%s channels=%ld total=%ld\n", bizdate, chans, total);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_exhaust_alert_batch.h - au au_exhaust_alert_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_EXHAUST_ALERT_BATCH_H
#define AU_EXHAUST_ALERT_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_exhaust_alert_batch_rec_t;
#endif /* AU_EXHAUST_ALERT_BATCH_H */

View file

@ -0,0 +1,50 @@
/*
* au_exhaust_alert_batch.pgc - au 한도 소진 경보 로그 배치 (커서 + userlog + XA).
*
* Cursor-scans cards that have burned >= 90% of their daily limit and emits a
* userlog alert for each, counting alerts, under one global XA transaction.
* No dbio writes. Usage: au_exhaust_alert_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_exhaust_alert_batch;
#include <userlog.h>
int main(int argc, char **argv)
{
long alerts = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
long h_bps;
EXEC SQL END DECLARE SECTION;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE exa CURSOR FOR
SELECT card_no, used_amount * 10000 / daily_limit
FROM card_limit
WHERE daily_limit > 0 AND used_amount * 10 >= daily_limit * 9
ORDER BY used_amount * 10000 / daily_limit DESC;
EXEC SQL OPEN exa;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH exa INTO :h_card, :h_bps;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE exa; tpabort(0); return 1; }
userlog("au_exhaust_alert 소진임박 card=%s %ldbps", h_card, h_bps);
alerts++;
}
EXEC SQL CLOSE exa;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_exhaust_alert_batch COMMIT: alerts=%ld\n", alerts);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_mcc_risk_batch.h - au au_mcc_risk_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_MCC_RISK_BATCH_H
#define AU_MCC_RISK_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_mcc_risk_batch_rec_t;
#endif /* AU_MCC_RISK_BATCH_H */

View file

@ -0,0 +1,52 @@
/*
* au_mcc_risk_batch.pgc - au 고위험 업종 매입 집계 배치 (GROUP BY HAVING 커서 + XA).
*
* Reports categories whose day's purchase volume exceeds 3,000,000 (HAVING),
* scanning count/sum/max per category under one global XA transaction. Read-only
* report. Usage: au_mcc_risk_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_mcc_risk_batch;
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long groups = 0, grand = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_cat[32];
long h_cnt, h_sum, h_max;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE mrk CURSOR FOR
SELECT category, count(*), coalesce(sum(amount),0), coalesce(max(amount),0)
FROM purchase WHERE biz_date = :h_bizdate
GROUP BY category HAVING coalesce(sum(amount),0) > 3000000
ORDER BY coalesce(sum(amount),0) DESC;
EXEC SQL OPEN mrk;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH mrk INTO :h_cat, :h_cnt, :h_sum, :h_max;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE mrk; tpabort(0); return 1; }
groups++;
grand += h_sum;
printf(" risk category=%s cnt=%ld sum=%ld max=%ld\n", h_cat, h_cnt, h_sum, h_max);
}
EXEC SQL CLOSE mrk;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_mcc_risk_batch COMMIT: bizdate=%s risk_groups=%ld grand=%ld\n", bizdate, groups, grand);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_month_limit_batch.h - au au_month_limit_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_MONTH_LIMIT_BATCH_H
#define AU_MONTH_LIMIT_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_month_limit_batch_rec_t;
#endif /* AU_MONTH_LIMIT_BATCH_H */

View file

@ -0,0 +1,37 @@
/*
* au_month_limit_batch.pgc - au 월 한도 소진 카드 집계 배치 (단일 집계 + XA).
*
* Aggregates in ONE query how many active cards have burned >= 80% of their
* daily limit and their total used amount, under one global XA transaction.
* No cursor. Usage: au_month_limit_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_month_limit_batch;
int main(int argc, char **argv)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_cards = 0, h_used = 0;
EXEC SQL END DECLARE SECTION;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*), coalesce(sum(used_amount),0)
INTO :h_cards, :h_used
FROM card_limit
WHERE daily_limit > 0 AND used_amount * 100 >= daily_limit * 80;
if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_month_limit_batch COMMIT: cards_over80=%ld used_total=%ld\n", h_cards, h_used);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_pertx_audit_batch.h - au au_pertx_audit_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_PERTX_AUDIT_BATCH_H
#define AU_PERTX_AUDIT_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_pertx_audit_batch_rec_t;
#endif /* AU_PERTX_AUDIT_BATCH_H */

View file

@ -0,0 +1,51 @@
/*
* au_pertx_audit_batch.pgc - au 건별 한도 초과 승인 감사 배치 (JOIN 커서 + XA).
*
* Joins the day's authorizations to card_limit and, for single transactions
* exceeding 20% of the daily limit (per-tx cap), logs a fraud row via dbio,
* all under ONE global XA transaction. Usage: au_pertx_audit_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_pertx_audit_batch;
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long rows = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_card[24];
long h_amt;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE pxa CURSOR FOR
SELECT a.card_no, a.amount
FROM au_authorization a JOIN card_limit l ON l.card_no = a.card_no
WHERE a.biz_date = :h_bizdate AND a.amount > l.daily_limit / 5
ORDER BY a.amount DESC;
EXEC SQL OPEN pxa;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH pxa INTO :h_card, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE pxa; tpabort(0); return 1; }
if (audb_insert_fraud(0, h_card, 500, "PERTX_OVER", bizdate) < 0) { EXEC SQL CLOSE pxa; tpabort(0); return 1; }
rows++;
}
EXEC SQL CLOSE pxa;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_pertx_audit_batch COMMIT: bizdate=%s flagged=%ld\n", bizdate, rows);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_reproc_queue_batch.h - au au_reproc_queue_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_REPROC_QUEUE_BATCH_H
#define AU_REPROC_QUEUE_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_reproc_queue_batch_rec_t;
#endif /* AU_REPROC_QUEUE_BATCH_H */

View file

@ -0,0 +1,49 @@
/*
* au_reproc_queue_batch.pgc - au 보류 승인 재처리 큐 배치 (커서 + dbio + XA).
*
* Cursor-scans the day's HELD (H) authorizations and re-approves each by driving
* the dbio status transition to A, under one global XA transaction.
* Usage: au_reproc_queue_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_reproc_queue_batch;
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
long rows = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_auth;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE rpq CURSOR FOR
SELECT auth_id FROM au_authorization
WHERE biz_date = :h_bizdate AND status = 'H' ORDER BY auth_id;
EXEC SQL OPEN rpq;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH rpq INTO :h_auth;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE rpq; tpabort(0); return 1; }
if (audb_update_auth_status(h_auth, "A") < 0) { EXEC SQL CLOSE rpq; tpabort(0); return 1; }
rows++;
}
EXEC SQL CLOSE rpq;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_reproc_queue_batch COMMIT: bizdate=%s reprocessed=%ld\n", bizdate, rows);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_stolen_sweep_batch.h - au au_stolen_sweep_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_STOLEN_SWEEP_BATCH_H
#define AU_STOLEN_SWEEP_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_stolen_sweep_batch_rec_t;
#endif /* AU_STOLEN_SWEEP_BATCH_H */

View file

@ -0,0 +1,48 @@
/*
* au_stolen_sweep_batch.pgc - au 도난/분실 카드 잔여승인 정리 배치 (커서 + XA).
*
* Cursor-scans blacklist entries flagged STOLEN/LOST and cancels each card's
* still-open (A/H) authorizations with an inner UPDATE, under one global XA
* transaction. Usage: au_stolen_sweep_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_stolen_sweep_batch;
int main(int argc, char **argv)
{
long cards = 0, cancelled = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
EXEC SQL END DECLARE SECTION;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE ssw CURSOR FOR
SELECT card_no FROM au_blacklist WHERE reason IN ('STOLEN','LOST') ORDER BY card_no;
EXEC SQL OPEN ssw;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH ssw INTO :h_card;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE ssw; tpabort(0); return 1; }
EXEC SQL UPDATE au_authorization SET status = 'C', updated_at = now()
WHERE card_no = :h_card AND status IN ('A','H');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ssw; tpabort(0); return 1; }
if (sqlca.sqlcode != 100) cancelled++;
cards++;
}
EXEC SQL CLOSE ssw;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_stolen_sweep_batch COMMIT: cards=%ld cancelled_batches=%ld\n", cards, cancelled);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_token_revoke_batch.h - au au_token_revoke_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_TOKEN_REVOKE_BATCH_H
#define AU_TOKEN_REVOKE_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_token_revoke_batch_rec_t;
#endif /* AU_TOKEN_REVOKE_BATCH_H */

View file

@ -0,0 +1,49 @@
/*
* au_token_revoke_batch.pgc - au 폐기 토큰 카드 원장 표시 배치 (커서 + XA).
*
* Cursor-scans TOKEN_REVOKED blacklist rows (card + reason) and marks each
* card_limit row REVOKED with an inner UPDATE, under one global XA transaction.
* Usage: au_token_revoke_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_token_revoke_batch;
int main(int argc, char **argv)
{
long seen = 0, marked = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24], h_reason[64];
EXEC SQL END DECLARE SECTION;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE trv CURSOR FOR
SELECT card_no, reason FROM au_blacklist
WHERE reason = 'TOKEN_REVOKED' ORDER BY created_at;
EXEC SQL OPEN trv;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH trv INTO :h_card, :h_reason;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE trv; tpabort(0); return 1; }
EXEC SQL UPDATE card_limit SET status = 'REVOKED', updated_at = now()
WHERE card_no = :h_card AND status <> 'REVOKED';
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE trv; tpabort(0); return 1; }
if (sqlca.sqlcode != 100) marked++;
seen++;
}
EXEC SQL CLOSE trv;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_token_revoke_batch COMMIT: revoked_tokens=%ld cards_marked=%ld\n", seen, marked);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,16 @@
/*
* au_whitelist_clean_batch.h - au au_whitelist_clean_batch (batch copybook / record layout).
* EXEC SQL INCLUDE .
* (.pgc) . dbio(libaudbio.a) , XA .
*/
#ifndef AU_WHITELIST_CLEAN_BATCH_H
#define AU_WHITELIST_CLEAN_BATCH_H
typedef struct {
char card_no[24];
char biz_date[16];
long amount;
long rows;
} au_au_whitelist_clean_batch_rec_t;
#endif /* AU_WHITELIST_CLEAN_BATCH_H */

View file

@ -0,0 +1,39 @@
/*
* au_whitelist_clean_batch.pgc - au 만료 폐기토큰 블랙 정리 배치 (집합 DELETE + XA).
*
* Counts, then set-deletes TOKEN_REVOKED blacklist rows older than 30 days in a
* single DELETE (auto-whitelisting expired revocations), under one global XA
* transaction. No cursor. Usage: au_whitelist_clean_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "au_dbio.h"
EXEC SQL INCLUDE au_whitelist_clean_batch;
int main(int argc, char **argv)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_stale = 0;
EXEC SQL END DECLARE SECTION;
(void)argc; (void)argv;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*) INTO :h_stale FROM au_blacklist
WHERE reason = 'TOKEN_REVOKED' AND created_at < now() - interval '30 days';
if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
EXEC SQL DELETE FROM au_blacklist
WHERE reason = 'TOKEN_REVOKED' AND created_at < now() - interval '30 days';
if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> au_whitelist_clean_batch COMMIT: expired_revocations_cleared=%ld\n", h_stale);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,20 @@
/*
* AUTH_PARTAPPR.h - au AUTH_PARTAPPR (copybook / record header).
* : /. : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef AUTH_PARTAPPR_H
#define AUTH_PARTAPPR_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_auth_partappr_rec_t;
void AUTH_PARTAPPR(TPSVCINFO *p);
#endif /* AUTH_PARTAPPR_H */

View file

@ -0,0 +1,44 @@
/*
* AUTH_PARTAPPR.pgc - au 모듈 서비스 AUTH_PARTAPPR (one service = one file).
* 부분 승인: 가용한도(일한도-사용액)가 요청액보다 작으면 가용한도만큼만
* 승인해 원장에 기록하고 한도를 차감한다. 전액 RC=0, 부분 RC=1, 불가 RC=2.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "AUTH_PARTAPPR.h"
void AUTH_PARTAPPR(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], merch[64], bizdate[16];
long amount, avail, appr, aid, rc;
EXEC SQL BEGIN DECLARE SECTION;
long h_daily = 0, h_used = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
amount = getl(b, T_AMOUNT);
if (card[0] == 0 || amount <= 0 || bizdate[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT daily_limit, used_amount INTO :h_daily, :h_used
FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
avail = h_daily - h_used;
if (avail <= 0) { setl(b, T_AMT1, 0); setl(b, T_RC, 2); OK(b); }
appr = (amount <= avail) ? amount : avail;
rc = (appr == amount) ? 0 : 1;
aid = audb_next_auth_id();
if (aid < 0) FAIL(b);
if (audb_insert_auth(aid, card, merch, appr, "PART", "AUTH", "A", bizdate) < 0) FAIL(b);
if (audb_limit_dec(card, appr) < 0) FAIL(b);
setl(b, T_ID2, aid);
setl(b, T_AMT1, appr);
setl(b, T_RC, rc);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* AUTH_REPROC_REQ.h - au AUTH_REPROC_REQ (copybook / record header).
* : ID. :
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef AUTH_REPROC_REQ_H
#define AUTH_REPROC_REQ_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_auth_reproc_req_rec_t;
void AUTH_REPROC_REQ(TPSVCINFO *p);
#endif /* AUTH_REPROC_REQ_H */

View file

@ -0,0 +1,34 @@
/*
* AUTH_REPROC_REQ.pgc - au 모듈 서비스 AUTH_REPROC_REQ (one service = one file).
* 승인 재처리 요청: 거절(D)/보류(H) 상태의 승인을 재처리 대상으로 다시
* 승인(A)으로 되돌린다(재시도 워크플로 진입). 대상 아님이면 RC=1.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "AUTH_REPROC_REQ.h"
void AUTH_REPROC_REQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_auth;
char h_old[4];
EXEC SQL END DECLARE SECTION;
h_auth = getl(b, T_ID2);
if (h_auth <= 0) FAIL(b);
memset(h_old, 0, sizeof(h_old));
EXEC SQL SELECT status INTO :h_old FROM au_authorization
WHERE auth_id = :h_auth AND status IN ('D','H');
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
EXEC SQL UPDATE au_authorization SET status = 'A', updated_at = now()
WHERE auth_id = :h_auth;
if (sqlca.sqlcode < 0) FAIL(b);
userlog("AUTH_REPROC_REQ auth=%ld %s->A", h_auth, h_old);
Bchg(b, T_STATUS, 0, h_old, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* AUTH_REPROC_STAT.h - au AUTH_REPROC_STAT (copybook / record header).
* : . : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef AUTH_REPROC_STAT_H
#define AUTH_REPROC_STAT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_auth_reproc_stat_rec_t;
void AUTH_REPROC_STAT(TPSVCINFO *p);
#endif /* AUTH_REPROC_STAT_H */

View file

@ -0,0 +1,32 @@
/*
* AUTH_REPROC_STAT.pgc - au 모듈 서비스 AUTH_REPROC_STAT (one service = one file).
* 재처리 현황: 영업일 승인 원장에서 보류(H) 대기 건수와, 갱신시각이 생성시각
* 이후로 밀린(재처리·상태변경된) 건수를 집계해 재처리 큐 상태를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "AUTH_REPROC_STAT.h"
void AUTH_REPROC_STAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_hold = 0, h_reproc = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*) FILTER (WHERE status = 'H'),
count(*) FILTER (WHERE updated_at > created_at)
INTO :h_hold, :h_reproc
FROM au_authorization WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG1, h_hold);
setl(b, T_ARG2, h_reproc);
setl(b, T_RC, (h_hold > 0) ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* BLACKLIST_COUNT.h - au BLACKLIST_COUNT (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef BLACKLIST_COUNT_H
#define BLACKLIST_COUNT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_blacklist_count_rec_t;
void BLACKLIST_COUNT(TPSVCINFO *p);
#endif /* BLACKLIST_COUNT_H */

View file

@ -0,0 +1,27 @@
/*
* BLACKLIST_COUNT.pgc - au 모듈 서비스 BLACKLIST_COUNT (one service = one file).
* 블랙리스트 통계: 전체 등재 수, 도난(REPORTED_STOLEN/STOLEN) 등재 수,
* 당일 신규 등재 수를 한 번의 집계로 뽑아 대시보드용으로 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "BLACKLIST_COUNT.h"
void BLACKLIST_COUNT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_total = 0, h_stolen = 0, h_today = 0;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*),
count(*) FILTER (WHERE reason IN ('REPORTED_STOLEN','STOLEN')),
count(*) FILTER (WHERE created_at >= current_date)
INTO :h_total, :h_stolen, :h_today
FROM au_blacklist;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_total);
setl(b, T_ARG1, h_stolen);
setl(b, T_ARG2, h_today);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* BLACKLIST_PROMO.h - au BLACKLIST_PROMO (copybook / record header).
* : . : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef BLACKLIST_PROMO_H
#define BLACKLIST_PROMO_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_blacklist_promo_rec_t;
void BLACKLIST_PROMO(TPSVCINFO *p);
#endif /* BLACKLIST_PROMO_H */

View file

@ -0,0 +1,44 @@
/*
* BLACKLIST_PROMO.pgc - au 모듈 서비스 BLACKLIST_PROMO (one service = one file).
* 블랙 승격 후보 조회: 영업일 부정 적발 점수 합이 1500 이상이면서 아직
* 블랙리스트에 없는 카드를 커서로 골라 "카드:점수합;" 후보 목록을 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "BLACKLIST_PROMO.h"
void BLACKLIST_PROMO(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], cand[256] = "", item[40];
long n = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_sum;
char h_bizdate[16], h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE cur_blpromo CURSOR FOR
SELECT f.card_no, sum(f.score) FROM au_fraud_log f
WHERE f.biz_date = :h_bizdate
AND f.card_no NOT IN (SELECT card_no FROM au_blacklist)
GROUP BY f.card_no HAVING sum(f.score) >= 1500
ORDER BY sum(f.score) DESC;
EXEC SQL OPEN cur_blpromo;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_blpromo INTO :h_card, :h_sum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_blpromo; FAIL(b); }
n++;
snprintf(item, sizeof(item), "%s:%ld;", h_card, h_sum);
if (strlen(cand) + strlen(item) < sizeof(cand) - 1) strcat(cand, item);
}
EXEC SQL CLOSE cur_blpromo;
Bchg(b, T_STR1, 0, cand, 0L);
setl(b, T_COUNT, n);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* BRAND_AUTH_STAT.h - au BRAND_AUTH_STAT (copybook / record header).
* : . : / /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef BRAND_AUTH_STAT_H
#define BRAND_AUTH_STAT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_brand_auth_stat_rec_t;
void BRAND_AUTH_STAT(TPSVCINFO *p);
#endif /* BRAND_AUTH_STAT_H */

View file

@ -0,0 +1,45 @@
/*
* BRAND_AUTH_STAT.pgc - au 모듈 서비스 BRAND_AUTH_STAT (one service = one file).
* 브랜드별 승인 통계: 영업일 승인 원장을 카드 선두자리(브랜드)로 그룹핑해
* 접수/승인 건수를 함께 뽑는 커서를 돌며 "선두=접수/승인;" 문자열을 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "BRAND_AUTH_STAT.h"
void BRAND_AUTH_STAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], mix[256] = "", item[48];
long total = 0, appr = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_all, h_ok;
char h_bizdate[16], h_lead[4];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE cur_brstat CURSOR FOR
SELECT substr(card_no, 1, 1), count(*),
count(*) FILTER (WHERE status IN ('A','M','S'))
FROM au_authorization WHERE biz_date = :h_bizdate
GROUP BY substr(card_no, 1, 1) ORDER BY 1;
EXEC SQL OPEN cur_brstat;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_brstat INTO :h_lead, :h_all, :h_ok;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_brstat; FAIL(b); }
total += h_all;
appr += h_ok;
snprintf(item, sizeof(item), "%s=%ld/%ld;", h_lead, h_all, h_ok);
if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item);
}
EXEC SQL CLOSE cur_brstat;
Bchg(b, T_STR1, 0, mix, 0L);
setl(b, T_COUNT, total);
setl(b, T_ARG1, appr);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* BRAND_POLICY.h - au BRAND_POLICY (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef BRAND_POLICY_H
#define BRAND_POLICY_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_brand_policy_rec_t;
void BRAND_POLICY(TPSVCINFO *p);
#endif /* BRAND_POLICY_H */

View file

@ -0,0 +1,41 @@
/*
* BRAND_POLICY.pgc - au 모듈 서비스 BRAND_POLICY (one service = one file).
* 브랜드별 승인 정책: 카드 선두자리로 브랜드(VISA/MC/AMEX/LOCAL)를 판별하고
* 당일 동일 브랜드 승인건수를 세어 AMEX 는 3건, 그 외는 8건 초과 시 제한한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "BRAND_POLICY.h"
void BRAND_POLICY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
const char *brand;
long cap;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0;
char h_card[24], h_lead[4];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
h_lead[0] = card[0]; h_lead[1] = 0;
switch (card[0]) {
case '4': brand = "VISA"; cap = 8; break;
case '5': brand = "MC"; cap = 8; break;
case '3': brand = "AMEX"; cap = 3; break;
default: brand = "LOCAL"; cap = 8; break;
}
EXEC SQL SELECT count(*) INTO :h_cnt FROM au_authorization
WHERE substr(card_no, 1, 1) = :h_lead
AND biz_date = current_date AND status = 'A';
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_ISSUER, 0, (char *)brand, 0L);
setl(b, T_COUNT, h_cnt);
setl(b, T_RC, (h_cnt > cap) ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CANCEL_AMT_INQ.h - au CANCEL_AMT_INQ (copybook / record header).
* : ID. : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CANCEL_AMT_INQ_H
#define CANCEL_AMT_INQ_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_cancel_amt_inq_rec_t;
void CANCEL_AMT_INQ(TPSVCINFO *p);
#endif /* CANCEL_AMT_INQ_H */

View file

@ -0,0 +1,36 @@
/*
* CANCEL_AMT_INQ.pgc - au 모듈 서비스 CANCEL_AMT_INQ (one service = one file).
* 취소가능액 조회: 원승인 금액에서 이미 취소된 누계를 빼 취소가능 잔액을
* 계산한다. 승인상태가 취소불가(취소완료/무효)면 RC=1 로 알린다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CANCEL_AMT_INQ.h"
void CANCEL_AMT_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long cancelable;
EXEC SQL BEGIN DECLARE SECTION;
long h_id, h_amt = 0, h_cxl = 0;
char h_status[4];
EXEC SQL END DECLARE SECTION;
h_id = getl(b, T_ID2);
if (h_id <= 0) FAIL(b);
memset(h_status, 0, sizeof(h_status));
EXEC SQL SELECT amount, status INTO :h_amt, :h_status
FROM au_authorization WHERE auth_id = :h_id;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_cxl
FROM au_cancel WHERE auth_id = :h_id;
if (sqlca.sqlcode < 0) FAIL(b);
cancelable = h_amt - h_cxl;
setl(b, T_AMT1, h_amt);
setl(b, T_AMT2, h_cxl);
setl(b, T_AMT3, cancelable);
setl(b, T_RC, (h_status[0] == 'C' || h_status[0] == 'V') ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CANCEL_FULL.h - au CANCEL_FULL (copybook / record header).
* : ID/. : ID/
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CANCEL_FULL_H
#define CANCEL_FULL_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_cancel_full_rec_t;
void CANCEL_FULL(TPSVCINFO *p);
#endif /* CANCEL_FULL_H */

View file

@ -0,0 +1,42 @@
/*
* CANCEL_FULL.pgc - au 모듈 서비스 CANCEL_FULL (one service = one file).
* 전액 취소: 승인(A)/매입확정(M) 상태의 원승인을 전액 취소 처리한다. 취소
* 원장(au_cancel FULL)을 채번·삽입하고 원승인 상태를 C 로 바꾼다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CANCEL_FULL.h"
void CANCEL_FULL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char reason[128];
EXEC SQL BEGIN DECLARE SECTION;
long h_auth, h_cid, h_amt = 0;
char h_bizdate[16], h_reason[128];
EXEC SQL END DECLARE SECTION;
h_auth = getl(b, T_ID2);
gets_(b, T_REASON, reason, sizeof(reason));
if (h_auth <= 0) FAIL(b);
strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0;
EXEC SQL SELECT amount, to_char(biz_date, 'YYYY-MM-DD')
INTO :h_amt, :h_bizdate
FROM au_authorization WHERE auth_id = :h_auth AND status IN ('A','M');
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT nextval('au_cancel_seq') INTO :h_cid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO au_cancel (cancel_id, auth_id, cancel_type, amount, reason, biz_date)
VALUES (:h_cid, :h_auth, 'FULL', :h_amt, :h_reason, :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE au_authorization SET status = 'C', updated_at = now()
WHERE auth_id = :h_auth;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ID1, h_cid);
setl(b, T_AMT1, h_amt);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CANCEL_HISTORY.h - au CANCEL_HISTORY (copybook / record header).
* : ID. : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CANCEL_HISTORY_H
#define CANCEL_HISTORY_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_cancel_history_rec_t;
void CANCEL_HISTORY(TPSVCINFO *p);
#endif /* CANCEL_HISTORY_H */

View file

@ -0,0 +1,42 @@
/*
* CANCEL_HISTORY.pgc - au 모듈 서비스 CANCEL_HISTORY (one service = one file).
* 승인건 취소 이력: 특정 원승인에 걸린 취소들을 시간순 커서로 순회하며
* "유형:금액;" 이력 문자열과 취소 건수/취소 합계를 조립해 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CANCEL_HISTORY.h"
void CANCEL_HISTORY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char hist[256] = "", item[48];
long cnt = 0, sum = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_auth, h_amt;
char h_type[12];
EXEC SQL END DECLARE SECTION;
h_auth = getl(b, T_ID2);
if (h_auth <= 0) FAIL(b);
EXEC SQL DECLARE cur_cxhist CURSOR FOR
SELECT cancel_type, amount FROM au_cancel
WHERE auth_id = :h_auth ORDER BY created_at;
EXEC SQL OPEN cur_cxhist;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_cxhist INTO :h_type, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cxhist; FAIL(b); }
cnt++;
sum += h_amt;
snprintf(item, sizeof(item), "%s:%ld;", h_type, h_amt);
if (strlen(hist) + strlen(item) < sizeof(hist) - 1) strcat(hist, item);
}
EXEC SQL CLOSE cur_cxhist;
Bchg(b, T_STR1, 0, hist, 0L);
setl(b, T_COUNT, cnt);
setl(b, T_AMT1, sum);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CANCEL_SUMMARY.h - au CANCEL_SUMMARY (copybook / record header).
* : . : ///
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CANCEL_SUMMARY_H
#define CANCEL_SUMMARY_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_cancel_summary_rec_t;
void CANCEL_SUMMARY(TPSVCINFO *p);
#endif /* CANCEL_SUMMARY_H */

View file

@ -0,0 +1,34 @@
/*
* CANCEL_SUMMARY.pgc - au 모듈 서비스 CANCEL_SUMMARY (one service = one file).
* 일별 취소 집계: 영업일 취소 원장을 전체/전액(FULL)/부분(PARTIAL)로 나눠
* 건수와 취소 합계를 한 번의 집계 쿼리로 뽑아 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CANCEL_SUMMARY.h"
void CANCEL_SUMMARY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0, h_sum = 0, h_full = 0, h_part = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(amount),0),
count(*) FILTER (WHERE cancel_type = 'FULL'),
count(*) FILTER (WHERE cancel_type = 'PARTIAL')
INTO :h_cnt, :h_sum, :h_full, :h_part
FROM au_cancel WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_AMT1, h_sum);
setl(b, T_ARG1, h_full);
setl(b, T_ARG2, h_part);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CARD_ACTIVATE.h - au CARD_ACTIVATE (copybook / record header).
* : /. :
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CARD_ACTIVATE_H
#define CARD_ACTIVATE_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_card_activate_rec_t;
void CARD_ACTIVATE(TPSVCINFO *p);
#endif /* CARD_ACTIVATE_H */

View file

@ -0,0 +1,33 @@
/*
* CARD_ACTIVATE.pgc - au 모듈 서비스 CARD_ACTIVATE (one service = one file).
* 카드 신규 활성화: 원장이 없으면 요청 한도(기본 100만원)로 생성하고, 이미
* 있으면 상태만 ACTIVE 로 되살리는 upsert 를 수행한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CARD_ACTIVATE.h"
void CARD_ACTIVATE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
long h_daily;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
h_daily = getl(b, T_AMT1);
if (card[0] == 0) FAIL(b);
if (h_daily <= 0) h_daily = 1000000;
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL INSERT INTO card_limit (card_no, daily_limit, status)
VALUES (:h_card, :h_daily, 'ACTIVE')
ON CONFLICT (card_no) DO UPDATE SET status = 'ACTIVE', updated_at = now();
if (sqlca.sqlcode < 0) FAIL(b);
userlog("CARD_ACTIVATE card=%s limit=%ld", card, h_daily);
setl(b, T_AMT1, h_daily);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CARD_EXPIRE_CHK.h - au CARD_EXPIRE_CHK (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CARD_EXPIRE_CHK_H
#define CARD_EXPIRE_CHK_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_card_expire_chk_rec_t;
void CARD_EXPIRE_CHK(TPSVCINFO *p);
#endif /* CARD_EXPIRE_CHK_H */

View file

@ -0,0 +1,37 @@
/*
* CARD_EXPIRE_CHK.pgc - au 모듈 서비스 CARD_EXPIRE_CHK (one service = one file).
* 카드 휴면(만료 근사) 점검: 마지막 승인 이후 경과일을 EPOCH 로 환산해
* 90일 넘게 활동이 없으면 휴면(RC=1)으로 판정한다. 활동건수도 함께 응답.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CARD_EXPIRE_CHK.h"
void CARD_EXPIRE_CHK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0, h_days = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT count(*),
CAST(coalesce(EXTRACT(EPOCH FROM now() - max(created_at)) / 86400, 9999) AS bigint)
INTO :h_cnt, :h_days
FROM au_authorization WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_ARG1, h_days);
if (h_days > 90) {
userlog("CARD_EXPIRE_CHK 휴면 card=%s %ld일", card, h_days);
setl(b, T_RC, 1);
} else {
setl(b, T_RC, 0);
}
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CARD_LOST_REG.h - au CARD_LOST_REG (copybook / record header).
* : /. :
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CARD_LOST_REG_H
#define CARD_LOST_REG_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_card_lost_reg_rec_t;
void CARD_LOST_REG(TPSVCINFO *p);
#endif /* CARD_LOST_REG_H */

View file

@ -0,0 +1,33 @@
/*
* CARD_LOST_REG.pgc - au 모듈 서비스 CARD_LOST_REG (one service = one file).
* 분실 등록: 카드 원장 상태를 LOST 로 바꾸고 블랙리스트에 LOST 사유로
* 등재(중복 시 무시)해 이후 승인 시도를 원천 차단한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CARD_LOST_REG.h"
void CARD_LOST_REG(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], reason[128];
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
gets_(b, T_REASON, reason, sizeof(reason));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL UPDATE card_limit SET status = 'LOST', updated_at = now()
WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO au_blacklist (card_no, reason)
VALUES (:h_card, 'LOST') ON CONFLICT (card_no) DO NOTHING;
if (sqlca.sqlcode < 0) FAIL(b);
userlog("CARD_LOST_REG 분실등록 card=%s reason=%s", card, reason);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CARD_REACTIVATE.h - au CARD_REACTIVATE (copybook / record header).
* : . :
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CARD_REACTIVATE_H
#define CARD_REACTIVATE_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_card_reactivate_rec_t;
void CARD_REACTIVATE(TPSVCINFO *p);
#endif /* CARD_REACTIVATE_H */

View file

@ -0,0 +1,34 @@
/*
* CARD_REACTIVATE.pgc - au 모듈 서비스 CARD_REACTIVATE (one service = one file).
* 카드 재활성화: 정지/동결/분실 상태의 원장을 ACTIVE 로 되돌리고, 분실·토큰
* 폐기 사유로 걸린 블랙리스트 항목을 삭제한다(도난은 보존). RC=재활성 여부.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CARD_REACTIVATE.h"
void CARD_REACTIVATE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
long restored;
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL UPDATE card_limit SET status = 'ACTIVE', updated_at = now()
WHERE card_no = :h_card AND status IN ('SUSPEND','FROZEN','LOST');
if (sqlca.sqlcode < 0) FAIL(b);
restored = (sqlca.sqlcode == 100) ? 0 : 1;
EXEC SQL DELETE FROM au_blacklist
WHERE card_no = :h_card AND reason IN ('LOST','TOKEN_REVOKED');
if (sqlca.sqlcode < 0) FAIL(b);
userlog("CARD_REACTIVATE card=%s restored=%ld", card, restored);
setl(b, T_RC, restored);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CARD_STATE_HIST.h - au CARD_STATE_HIST (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CARD_STATE_HIST_H
#define CARD_STATE_HIST_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_card_state_hist_rec_t;
void CARD_STATE_HIST(TPSVCINFO *p);
#endif /* CARD_STATE_HIST_H */

View file

@ -0,0 +1,43 @@
/*
* CARD_STATE_HIST.pgc - au 모듈 서비스 CARD_STATE_HIST (one service = one file).
* 카드 승인상태 이력 분포: 카드의 전체 승인 건을 상태(status)별로 집계하는
* 커서를 돌며 "상태=건수;" 분포 문자열과 총건수/상태종류 수를 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CARD_STATE_HIST.h"
void CARD_STATE_HIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], dist[256] = "", item[32];
long total = 0, kinds = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt;
char h_card[24], h_st[4];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL DECLARE cur_cshist CURSOR FOR
SELECT status, count(*) FROM au_authorization
WHERE card_no = :h_card GROUP BY status ORDER BY count(*) DESC;
EXEC SQL OPEN cur_cshist;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_cshist INTO :h_st, :h_cnt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cshist; FAIL(b); }
kinds++;
total += h_cnt;
snprintf(item, sizeof(item), "%s=%ld;", h_st, h_cnt);
if (strlen(dist) + strlen(item) < sizeof(dist) - 1) strcat(dist, item);
}
EXEC SQL CLOSE cur_cshist;
Bchg(b, T_STR1, 0, dist, 0L);
setl(b, T_COUNT, total);
setl(b, T_ARG1, kinds);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CARD_STOLEN_REG.h - au CARD_STOLEN_REG (copybook / record header).
* : //. : + ID
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CARD_STOLEN_REG_H
#define CARD_STOLEN_REG_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_card_stolen_reg_rec_t;
void CARD_STOLEN_REG(TPSVCINFO *p);
#endif /* CARD_STOLEN_REG_H */

View file

@ -0,0 +1,40 @@
/*
* CARD_STOLEN_REG.pgc - au 모듈 서비스 CARD_STOLEN_REG (one service = one file).
* 도난 등록: 원장 상태를 STOLEN 으로 전환하고 블랙리스트를 STOLEN 사유로
* 갱신한 뒤, 최고점(999) 부정거래 로그를 남겨 사후추적 근거를 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CARD_STOLEN_REG.h"
void CARD_STOLEN_REG(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], reason[128], bizdate[16];
long fid;
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
gets_(b, T_REASON, reason, sizeof(reason));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (card[0] == 0 || bizdate[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL UPDATE card_limit SET status = 'STOLEN', updated_at = now()
WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO au_blacklist (card_no, reason)
VALUES (:h_card, 'STOLEN')
ON CONFLICT (card_no) DO UPDATE SET reason = 'STOLEN';
if (sqlca.sqlcode < 0) FAIL(b);
fid = audb_insert_fraud(0, card, 999, "CARD_STOLEN", bizdate);
if (fid < 0) FAIL(b);
userlog("CARD_STOLEN_REG 도난등록 card=%s fraud=%ld", card, fid);
setl(b, T_ID1, fid);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CHANNEL_POLICY.h - au CHANNEL_POLICY (copybook / record header).
* : . : /(ALLOW/THROTTLE)
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CHANNEL_POLICY_H
#define CHANNEL_POLICY_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_channel_policy_rec_t;
void CHANNEL_POLICY(TPSVCINFO *p);
#endif /* CHANNEL_POLICY_H */

View file

@ -0,0 +1,39 @@
/*
* CHANNEL_POLICY.pgc - au 모듈 서비스 CHANNEL_POLICY (one service = one file).
* 채널별 승인 정책: 매입 원장에서 당일 해당 채널(POS/EDC/EDI/FOREIGN)의
* 취소율(bps)을 계산해 15% 초과면 THROTTLE, 아니면 ALLOW 로 판정한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CHANNEL_POLICY.h"
void CHANNEL_POLICY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char chan[16];
long rate = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_all = 0, h_cxl = 0;
char h_chan[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_CHANNEL, chan, sizeof(chan));
if (chan[0] == 0) strcpy(chan, "POS");
strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0;
EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'C')
INTO :h_all, :h_cxl
FROM purchase WHERE channel = :h_chan AND biz_date = current_date;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_all > 0) rate = (h_cxl * 10000L) / h_all;
setl(b, T_COUNT, h_all);
setl(b, T_ARG1, rate);
if (rate > 1500) {
Bchg(b, T_STR1, 0, "THROTTLE", 0L);
setl(b, T_RC, 1);
} else {
Bchg(b, T_STR1, 0, "ALLOW", 0L);
setl(b, T_RC, 0);
}
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* CHANNEL_STAT.h - au CHANNEL_STAT (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef CHANNEL_STAT_H
#define CHANNEL_STAT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_channel_stat_rec_t;
void CHANNEL_STAT(TPSVCINFO *p);
#endif /* CHANNEL_STAT_H */

View file

@ -0,0 +1,45 @@
/*
* CHANNEL_STAT.pgc - au 모듈 서비스 CHANNEL_STAT (one service = one file).
* 채널별 매입 통계: 영업일 매입을 채널별로 그룹핑하는 커서를 순회하며
* "채널=건수/금액;" 분포 문자열과 총건수, 최다 매출채널을 산출한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "CHANNEL_STAT.h"
void CHANNEL_STAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], mix[256] = "", top_chan[16] = "", item[48];
long total = 0, top_amt = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt, h_amt;
char h_bizdate[16], h_chan[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE cur_chstat CURSOR FOR
SELECT channel, count(*), coalesce(sum(amount),0)
FROM purchase WHERE biz_date = :h_bizdate
GROUP BY channel ORDER BY coalesce(sum(amount),0) DESC;
EXEC SQL OPEN cur_chstat;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_chstat INTO :h_chan, :h_cnt, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_chstat; FAIL(b); }
total += h_cnt;
if (h_amt > top_amt) { top_amt = h_amt; strncpy(top_chan, h_chan, sizeof(top_chan)-1); }
snprintf(item, sizeof(item), "%s=%ld/%ld;", h_chan, h_cnt, h_amt);
if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item);
}
EXEC SQL CLOSE cur_chstat;
Bchg(b, T_STR1, 0, mix, 0L);
Bchg(b, T_CHANNEL, 0, top_chan, 0L);
setl(b, T_COUNT, total);
setl(b, T_GROSS, top_amt);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* FRAUD_CASE_CLOSE.h - au FRAUD_CASE_CLOSE (copybook / record header).
* : ID. :
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef FRAUD_CASE_CLOSE_H
#define FRAUD_CASE_CLOSE_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_fraud_case_close_rec_t;
void FRAUD_CASE_CLOSE(TPSVCINFO *p);
#endif /* FRAUD_CASE_CLOSE_H */

View file

@ -0,0 +1,27 @@
/*
* FRAUD_CASE_CLOSE.pgc - au 모듈 서비스 FRAUD_CASE_CLOSE (one service = one file).
* 부정 케이스 종결: 미종결 부정로그의 사유 뒤에 '/CLOSED' 를 붙여 종결 표시
* 한다. 이미 종결됐거나 존재하지 않으면 RC=1 로 알린다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "FRAUD_CASE_CLOSE.h"
void FRAUD_CASE_CLOSE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_fid;
EXEC SQL END DECLARE SECTION;
h_fid = getl(b, T_ID1);
if (h_fid <= 0) FAIL(b);
EXEC SQL UPDATE au_fraud_log SET reason = reason || '/CLOSED'
WHERE fraud_id = :h_fid AND reason NOT LIKE '%CLOSED%';
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
userlog("FRAUD_CASE_CLOSE fraud=%ld 종결", h_fid);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* FRAUD_CASE_LIST.h - au FRAUD_CASE_LIST (copybook / record header).
* : . : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef FRAUD_CASE_LIST_H
#define FRAUD_CASE_LIST_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_fraud_case_list_rec_t;
void FRAUD_CASE_LIST(TPSVCINFO *p);
#endif /* FRAUD_CASE_LIST_H */

View file

@ -0,0 +1,41 @@
/*
* FRAUD_CASE_LIST.pgc - au 모듈 서비스 FRAUD_CASE_LIST (one service = one file).
* 부정 케이스 상위목록: 영업일 부정로그를 점수 내림차순 상위 5건 커서로
* 뽑아 "카드:점수;" 목록 문자열과 케이스 건수를 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "FRAUD_CASE_LIST.h"
void FRAUD_CASE_LIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], list[256] = "", item[40];
long cnt = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_score;
char h_bizdate[16], h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE cur_fclist CURSOR FOR
SELECT card_no, score FROM au_fraud_log
WHERE biz_date = :h_bizdate ORDER BY score DESC LIMIT 5;
EXEC SQL OPEN cur_fclist;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_fclist INTO :h_card, :h_score;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fclist; FAIL(b); }
cnt++;
snprintf(item, sizeof(item), "%s:%ld;", h_card, h_score);
if (strlen(list) + strlen(item) < sizeof(list) - 1) strcat(list, item);
}
EXEC SQL CLOSE cur_fclist;
Bchg(b, T_STR1, 0, list, 0L);
setl(b, T_COUNT, cnt);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* FRAUD_RULE_INTL.h - au FRAUD_RULE_INTL (copybook / record header).
* : /. : /ID
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef FRAUD_RULE_INTL_H
#define FRAUD_RULE_INTL_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_fraud_rule_intl_rec_t;
void FRAUD_RULE_INTL(TPSVCINFO *p);
#endif /* FRAUD_RULE_INTL_H */

View file

@ -0,0 +1,43 @@
/*
* FRAUD_RULE_INTL.pgc - au 모듈 서비스 FRAUD_RULE_INTL (one service = one file).
* 고액(해외성) 이상거래 룰: 최근 1일 100만원 초과 승인이 3건 이상이면
* 고액 반복으로 보고 점수(720 + 초과*25)로 적발한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "FRAUD_RULE_INTL.h"
void FRAUD_RULE_INTL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], bizdate[16];
long score, fid;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0, h_sum = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (card[0] == 0 || bizdate[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_sum
FROM au_authorization
WHERE card_no = :h_card AND amount > 1000000
AND created_at >= now() - interval '1 day';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);
if (h_cnt < 3) { setl(b, T_RC, 0); OK(b); }
score = 720 + (h_cnt - 3) * 25;
if (score > 999) score = 999;
fid = audb_insert_fraud(0, card, score, "RULE_HIGH_AMT", bizdate);
if (fid < 0) FAIL(b);
userlog("FRAUD_RULE_INTL card=%s cnt=%ld sum=%ld", card, h_cnt, h_sum);
setl(b, T_ID1, fid);
setl(b, T_RC, 1);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* FRAUD_RULE_MCC.h - au FRAUD_RULE_MCC (copybook / record header).
* : /. : /ID
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef FRAUD_RULE_MCC_H
#define FRAUD_RULE_MCC_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_fraud_rule_mcc_rec_t;
void FRAUD_RULE_MCC(TPSVCINFO *p);
#endif /* FRAUD_RULE_MCC_H */

View file

@ -0,0 +1,41 @@
/*
* FRAUD_RULE_MCC.pgc - au 모듈 서비스 FRAUD_RULE_MCC (one service = one file).
* 관찰가맹 이상거래 룰: 카드의 당일 승인 중 상태가 WATCH 인 가맹점 건수를
* 서브쿼리로 세어 2건 이상이면 고위험 가맹 반복으로 적발(점수 760)한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "FRAUD_RULE_MCC.h"
void FRAUD_RULE_MCC(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], bizdate[16];
long fid;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0;
char h_card[24], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (card[0] == 0 || bizdate[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_cnt
FROM au_authorization a
WHERE a.card_no = :h_card AND a.biz_date = :h_bizdate
AND a.merchant_id IN (SELECT merchant_id FROM merchant WHERE status = 'WATCH');
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
if (h_cnt < 2) { setl(b, T_RC, 0); OK(b); }
fid = audb_insert_fraud(0, card, 760, "RULE_WATCH_MCC", bizdate);
if (fid < 0) FAIL(b);
userlog("FRAUD_RULE_MCC card=%s watch=%ld", card, h_cnt);
setl(b, T_ID1, fid);
setl(b, T_RC, 1);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* FRAUD_RULE_NIGHT.h - au FRAUD_RULE_NIGHT (copybook / record header).
* : /. : /ID
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef FRAUD_RULE_NIGHT_H
#define FRAUD_RULE_NIGHT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_fraud_rule_night_rec_t;
void FRAUD_RULE_NIGHT(TPSVCINFO *p);
#endif /* FRAUD_RULE_NIGHT_H */

View file

@ -0,0 +1,42 @@
/*
* FRAUD_RULE_NIGHT.pgc - au 모듈 서비스 FRAUD_RULE_NIGHT (one service = one file).
* 심야 이상거래 룰: 최근 3일 심야시간대(23~06시) 승인 시도가 6건 이상이면
* 건수 비례 점수(650 + 초과*15)로 부정거래 로그에 적발한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "FRAUD_RULE_NIGHT.h"
void FRAUD_RULE_NIGHT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24], bizdate[16];
long score, fid;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (card[0] == 0 || bizdate[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_cnt FROM au_authorization
WHERE card_no = :h_card
AND created_at >= now() - interval '3 days'
AND (EXTRACT(HOUR FROM created_at) >= 23 OR EXTRACT(HOUR FROM created_at) < 6);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
if (h_cnt < 6) { setl(b, T_RC, 0); OK(b); }
score = 650 + (h_cnt - 6) * 15;
if (score > 999) score = 999;
fid = audb_insert_fraud(0, card, score, "RULE_NIGHT", bizdate);
if (fid < 0) FAIL(b);
userlog("FRAUD_RULE_NIGHT card=%s night=%ld score=%ld", card, h_cnt, score);
setl(b, T_ID1, fid);
setl(b, T_RC, 1);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* FRAUD_SCORE_SUM.h - au FRAUD_SCORE_SUM (copybook / record header).
* : . : ///
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef FRAUD_SCORE_SUM_H
#define FRAUD_SCORE_SUM_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_fraud_score_sum_rec_t;
void FRAUD_SCORE_SUM(TPSVCINFO *p);
#endif /* FRAUD_SCORE_SUM_H */

View file

@ -0,0 +1,38 @@
/*
* FRAUD_SCORE_SUM.pgc - au 모듈 서비스 FRAUD_SCORE_SUM (one service = one file).
* 카드 위험도 종합: 카드의 부정 적발 건수/점수합/최고점수를 집계해 최고점수
* 구간으로 LOW/MID/HIGH/CRITICAL 위험등급을 매겨 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "FRAUD_SCORE_SUM.h"
void FRAUD_SCORE_SUM(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
const char *grade;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0, h_sum = 0, h_max = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(score),0), coalesce(max(score),0)
INTO :h_cnt, :h_sum, :h_max
FROM au_fraud_log WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_max >= 900) grade = "CRITICAL";
else if (h_max >= 700) grade = "HIGH";
else if (h_max >= 400) grade = "MID";
else grade = "LOW";
setl(b, T_COUNT, h_cnt);
setl(b, T_ARG1, h_sum);
setl(b, T_ARG2, h_max);
Bchg(b, T_STR1, 0, (char *)grade, 0L);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* KEY_ROTATE_LOG.h - au KEY_ROTATE_LOG (copybook / record header).
* : (T_ARG1). : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef KEY_ROTATE_LOG_H
#define KEY_ROTATE_LOG_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_key_rotate_log_rec_t;
void KEY_ROTATE_LOG(TPSVCINFO *p);
#endif /* KEY_ROTATE_LOG_H */

View file

@ -0,0 +1,30 @@
/*
* KEY_ROTATE_LOG.pgc - au 모듈 서비스 KEY_ROTATE_LOG (one service = one file).
* 암호키 회전 로그: 요청 키버전(T_ARG1)의 차기 버전을 산출하고 키식별자
* 문자열에서 파생 체크값(문자합 mod 9973)을 계산한다(순수 연산, DB 접근 없음).
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "KEY_ROTATE_LOG.h"
void KEY_ROTATE_LOG(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char keyid[64];
long cur, next, chk = 0;
int i, len;
cur = getl(b, T_ARG1);
gets_(b, T_KEY1, keyid, sizeof(keyid));
if (keyid[0] == 0) FAIL(b);
next = (cur < 0) ? 1 : cur + 1;
len = (int)strlen(keyid);
for (i = 0; i < len; i++)
chk = (chk * 31 + (unsigned char)keyid[i]) % 9973;
userlog("KEY_ROTATE_LOG key=%s v%ld->v%ld chk=%ld", keyid, cur, next, chk);
setl(b, T_ARG2, next);
setl(b, T_ARG3, chk);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_ADJUST.h - au LIMIT_ADJUST (copybook / record header).
* : /(T_DELTA). : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_ADJUST_H
#define LIMIT_ADJUST_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_adjust_rec_t;
void LIMIT_ADJUST(TPSVCINFO *p);
#endif /* LIMIT_ADJUST_H */

View file

@ -0,0 +1,34 @@
/*
* LIMIT_ADJUST.pgc - au 모듈 서비스 LIMIT_ADJUST (one service = one file).
* 한도 증감 조정: 요청 증감액(T_DELTA)을 일한도에 가감하되 음수 한도가 되지
* 않도록 조건부 UPDATE 한다. 조건 불충족(음수화)이면 RC=1, 성공 시 새 한도.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_ADJUST.h"
void LIMIT_ADJUST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
long h_delta, h_new = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
h_delta = getl(b, T_DELTA);
if (card[0] == 0 || h_delta == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL UPDATE card_limit
SET daily_limit = daily_limit + :h_delta, updated_at = now()
WHERE card_no = :h_card AND daily_limit + :h_delta >= 0;
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT daily_limit INTO :h_new FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_AMT1, h_new);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_CUMUL_INQ.h - au LIMIT_CUMUL_INQ (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_CUMUL_INQ_H
#define LIMIT_CUMUL_INQ_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_cumul_inq_rec_t;
void LIMIT_CUMUL_INQ(TPSVCINFO *p);
#endif /* LIMIT_CUMUL_INQ_H */

View file

@ -0,0 +1,31 @@
/*
* LIMIT_CUMUL_INQ.pgc - au 모듈 서비스 LIMIT_CUMUL_INQ (one service = one file).
* 전(全)기간 누적 사용 조회: 카드의 전체 승인 이력에서 누적 건수/누적 합계/
* 최대 단건 금액을 뽑아 준다(기간 제한 없음).
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_CUMUL_INQ.h"
void LIMIT_CUMUL_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0, h_sum = 0, h_max = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(max(amount),0)
INTO :h_cnt, :h_sum, :h_max
FROM au_authorization WHERE card_no = :h_card AND status IN ('A','M','S');
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_AMT1, h_sum);
setl(b, T_AMT2, h_max);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_DAILY_INQ.h - au LIMIT_DAILY_INQ (copybook / record header).
* : . : ////
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_DAILY_INQ_H
#define LIMIT_DAILY_INQ_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_daily_inq_rec_t;
void LIMIT_DAILY_INQ(TPSVCINFO *p);
#endif /* LIMIT_DAILY_INQ_H */

View file

@ -0,0 +1,34 @@
/*
* LIMIT_DAILY_INQ.pgc - au 모듈 서비스 LIMIT_DAILY_INQ (one service = one file).
* 일 한도 상세 조회: 원장에서 일한도/사용액/임시한도/상태를 한 번에 읽어
* 가용액(일한도+임시한도-사용액)까지 계산해 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_DAILY_INQ.h"
void LIMIT_DAILY_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
long h_daily = 0, h_used = 0, h_temp = 0;
char h_card[24], h_status[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
memset(h_status, 0, sizeof(h_status));
EXEC SQL SELECT daily_limit, used_amount, temp_limit, status
INTO :h_daily, :h_used, :h_temp, :h_status
FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
setl(b, T_AMT1, h_daily);
setl(b, T_AMT2, h_used);
setl(b, T_AMT3, h_temp);
setl(b, T_AMT4, h_daily + h_temp - h_used);
Bchg(b, T_STATUS, 0, h_status, 0L);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_EXHAUST_LST.h - au LIMIT_EXHAUST_LST (copybook / record header).
* : (T_ARG1). : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_EXHAUST_LST_H
#define LIMIT_EXHAUST_LST_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_exhaust_lst_rec_t;
void LIMIT_EXHAUST_LST(TPSVCINFO *p);
#endif /* LIMIT_EXHAUST_LST_H */

View file

@ -0,0 +1,44 @@
/*
* LIMIT_EXHAUST_LST.pgc - au 모듈 서비스 LIMIT_EXHAUST_LST (one service = one file).
* 한도 소진 카드 목록: 요청 기준율(bps, 기본 9000=90%) 이상으로 한도를 소진한
* 카드를 커서로 골라 "카드:소진율;" 목록과 대상 건수를 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_EXHAUST_LST.h"
void LIMIT_EXHAUST_LST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char list[256] = "", item[40];
long n = 0, thr;
EXEC SQL BEGIN DECLARE SECTION;
long h_thr, h_bps;
char h_card[24];
EXEC SQL END DECLARE SECTION;
thr = getl(b, T_ARG1);
if (thr <= 0) thr = 9000;
h_thr = thr;
EXEC SQL DECLARE cur_exlst CURSOR FOR
SELECT card_no, (used_amount * 10000 / nullif(daily_limit, 0)) AS bps
FROM card_limit
WHERE daily_limit > 0 AND used_amount * 10000 / daily_limit >= :h_thr
ORDER BY bps DESC;
EXEC SQL OPEN cur_exlst;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_exlst INTO :h_card, :h_bps;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_exlst; FAIL(b); }
n++;
snprintf(item, sizeof(item), "%s:%ld;", h_card, h_bps);
if (strlen(list) + strlen(item) < sizeof(list) - 1) strcat(list, item);
}
EXEC SQL CLOSE cur_exlst;
Bchg(b, T_STR1, 0, list, 0L);
setl(b, T_COUNT, n);
setl(b, T_ARG1, thr);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_FREEZE.h - au LIMIT_FREEZE (copybook / record header).
* : . : (T_RC)
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_FREEZE_H
#define LIMIT_FREEZE_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_freeze_rec_t;
void LIMIT_FREEZE(TPSVCINFO *p);
#endif /* LIMIT_FREEZE_H */

View file

@ -0,0 +1,31 @@
/*
* LIMIT_FREEZE.pgc - au 모듈 서비스 LIMIT_FREEZE (one service = one file).
* 한도 동결: ACTIVE 상태의 카드 원장만 FROZEN 으로 전환한다. 이미 다른
* 상태면 대상 아님(RC=1)으로 응답하고 관제 로그를 남긴다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_FREEZE.h"
void LIMIT_FREEZE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL UPDATE card_limit SET status = 'FROZEN', updated_at = now()
WHERE card_no = :h_card AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) {
userlog("LIMIT_FREEZE 대상아님 card=%s", card);
setl(b, T_RC, 1);
OK(b);
}
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_MONTH_CHK.h - au LIMIT_MONTH_CHK (copybook / record header).
* : /. : //(T_RC)
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_MONTH_CHK_H
#define LIMIT_MONTH_CHK_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_month_chk_rec_t;
void LIMIT_MONTH_CHK(TPSVCINFO *p);
#endif /* LIMIT_MONTH_CHK_H */

View file

@ -0,0 +1,38 @@
/*
* LIMIT_MONTH_CHK.pgc - au 모듈 서비스 LIMIT_MONTH_CHK (one service = one file).
* 월 한도 점검: 이번 달 승인(A/M/S) 누적액에 요청금액을 더해 월 한도
* (일한도 x 25 영업일 근사)를 넘는지 판정한다(RC=1 초과).
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_MONTH_CHK.h"
void LIMIT_MONTH_CHK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
long amount, cap = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_month = 0, h_daily = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
amount = getl(b, T_AMOUNT);
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_month
FROM au_authorization
WHERE card_no = :h_card AND status IN ('A','M','S')
AND biz_date >= date_trunc('month', current_date);
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT daily_limit INTO :h_daily FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
cap = h_daily * 25;
setl(b, T_AMT1, h_month);
setl(b, T_AMT2, cap);
setl(b, T_RC, (h_month + amount > cap) ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_MONTH_INQ.h - au LIMIT_MONTH_INQ (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_MONTH_INQ_H
#define LIMIT_MONTH_INQ_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_month_inq_rec_t;
void LIMIT_MONTH_INQ(TPSVCINFO *p);
#endif /* LIMIT_MONTH_INQ_H */

View file

@ -0,0 +1,34 @@
/*
* LIMIT_MONTH_INQ.pgc - au 모듈 서비스 LIMIT_MONTH_INQ (one service = one file).
* 월 누적 사용 조회: 이번 달 승인건 기준으로 건수/합계/평균단가를 한 번의
* 집계로 산출해 내려준다(카드 실적 리포트 용).
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_MONTH_INQ.h"
void LIMIT_MONTH_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt = 0, h_sum = 0, h_avg = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(amount),0),
CAST(coalesce(avg(amount),0) AS bigint)
INTO :h_cnt, :h_sum, :h_avg
FROM au_authorization
WHERE card_no = :h_card AND status IN ('A','M','S')
AND biz_date >= date_trunc('month', current_date);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_AMT1, h_sum);
setl(b, T_AMT2, h_avg);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_OVER_ALERT.h - au LIMIT_OVER_ALERT (copybook / record header).
* : . : (bps)//
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_OVER_ALERT_H
#define LIMIT_OVER_ALERT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_over_alert_rec_t;
void LIMIT_OVER_ALERT(TPSVCINFO *p);
#endif /* LIMIT_OVER_ALERT_H */

View file

@ -0,0 +1,37 @@
/*
* LIMIT_OVER_ALERT.pgc - au 모듈 서비스 LIMIT_OVER_ALERT (one service = one file).
* 한도 소진 임박 알림: 사용액/일한도를 bps 로 환산해 90% 이상이면 경보
* (RC=1)와 관제 로그를 내고, 잔여 한도액도 함께 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_OVER_ALERT.h"
void LIMIT_OVER_ALERT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
long bps = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_daily = 0, h_used = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT daily_limit, used_amount INTO :h_daily, :h_used
FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
if (h_daily > 0) bps = (h_used * 10000L) / h_daily;
setl(b, T_ARG1, bps);
setl(b, T_AMT1, h_daily - h_used);
if (bps >= 9000) {
userlog("LIMIT_OVER_ALERT 소진임박 card=%s %ldbps", card, bps);
setl(b, T_RC, 1);
} else {
setl(b, T_RC, 0);
}
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_PERTX_CHK.h - au LIMIT_PERTX_CHK (copybook / record header).
* : /. : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_PERTX_CHK_H
#define LIMIT_PERTX_CHK_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_pertx_chk_rec_t;
void LIMIT_PERTX_CHK(TPSVCINFO *p);
#endif /* LIMIT_PERTX_CHK_H */

View file

@ -0,0 +1,37 @@
/*
* LIMIT_PERTX_CHK.pgc - au 모듈 서비스 LIMIT_PERTX_CHK (one service = one file).
* 건별(1회) 한도 점검: 일한도의 20% 를 건별 상한으로 삼아 요청 단건 금액이
* 이를 넘으면 차단(RC=1)한다. 초과 시 관제 로그를 남긴다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_PERTX_CHK.h"
void LIMIT_PERTX_CHK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
long amount, cap;
EXEC SQL BEGIN DECLARE SECTION;
long h_daily = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
amount = getl(b, T_AMOUNT);
if (card[0] == 0 || amount <= 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT daily_limit INTO :h_daily FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode == 100) FAIL(b);
if (sqlca.sqlcode < 0) FAIL(b);
cap = h_daily / 5;
setl(b, T_AMT1, cap);
if (amount > cap) {
userlog("LIMIT_PERTX_CHK 초과 card=%s req=%ld cap=%ld", card, amount, cap);
setl(b, T_RC, 1);
} else {
setl(b, T_RC, 0);
}
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_TIER_INQ.h - au LIMIT_TIER_INQ (copybook / record header).
* : . : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_TIER_INQ_H
#define LIMIT_TIER_INQ_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_tier_inq_rec_t;
void LIMIT_TIER_INQ(TPSVCINFO *p);
#endif /* LIMIT_TIER_INQ_H */

View file

@ -0,0 +1,34 @@
/*
* LIMIT_TIER_INQ.pgc - au 모듈 서비스 LIMIT_TIER_INQ (one service = one file).
* 한도 등급 조회: 일한도 구간으로 BASIC/SILVER/GOLD/PLATINUM 등급을 매겨
* 등급 문자열과 원 한도를 함께 응답한다(마케팅/우대 판정 근거).
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_TIER_INQ.h"
void LIMIT_TIER_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
const char *tier;
EXEC SQL BEGIN DECLARE SECTION;
long h_daily = 0;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL SELECT daily_limit INTO :h_daily FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
if (h_daily <= 1000000) tier = "BASIC";
else if (h_daily <= 3000000) tier = "SILVER";
else if (h_daily <= 5000000) tier = "GOLD";
else tier = "PLATINUM";
Bchg(b, T_STR1, 0, (char *)tier, 0L);
setl(b, T_AMT1, h_daily);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* LIMIT_UNFREEZE.h - au LIMIT_UNFREEZE (copybook / record header).
* : . : (T_RC)
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef LIMIT_UNFREEZE_H
#define LIMIT_UNFREEZE_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_limit_unfreeze_rec_t;
void LIMIT_UNFREEZE(TPSVCINFO *p);
#endif /* LIMIT_UNFREEZE_H */

View file

@ -0,0 +1,26 @@
/*
* LIMIT_UNFREEZE.pgc - au 모듈 서비스 LIMIT_UNFREEZE (one service = one file).
* 한도 동결 해제: FROZEN 상태의 카드 원장만 ACTIVE 로 되돌린다. 대상이
* 없으면 RC=1, 성공하면 RC=0 을 rowcount 로 판정해 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "LIMIT_UNFREEZE.h"
void LIMIT_UNFREEZE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char card[24];
EXEC SQL BEGIN DECLARE SECTION;
char h_card[24];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, card, sizeof(card));
if (card[0] == 0) FAIL(b);
strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
EXEC SQL UPDATE card_limit SET status = 'ACTIVE', updated_at = now()
WHERE card_no = :h_card AND status = 'FROZEN';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_RC, (sqlca.sqlcode == 100) ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* MCC_AUTH_STAT.h - au MCC_AUTH_STAT (copybook / record header).
* : . : /
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef MCC_AUTH_STAT_H
#define MCC_AUTH_STAT_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_mcc_auth_stat_rec_t;
void MCC_AUTH_STAT(TPSVCINFO *p);
#endif /* MCC_AUTH_STAT_H */

View file

@ -0,0 +1,44 @@
/*
* MCC_AUTH_STAT.pgc - au 모듈 서비스 MCC_AUTH_STAT (one service = one file).
* 업종별 매입 금액 통계: 영업일 매입을 업종(category)별 합계로 그룹핑하는
* 커서를 돌며 "업종=금액;" 문자열과 총액, 최다 매출 업종을 뽑는다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
*/
#include "MCC_AUTH_STAT.h"
void MCC_AUTH_STAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], mix[256] = "", top_cat[32] = "", item[48];
long grand = 0, top = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_amt;
char h_bizdate[16], h_cat[32];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (bizdate[0] == 0) FAIL(b);
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE cur_mccstat CURSOR FOR
SELECT category, coalesce(sum(amount),0)
FROM purchase WHERE biz_date = :h_bizdate
GROUP BY category ORDER BY coalesce(sum(amount),0) DESC LIMIT 8;
EXEC SQL OPEN cur_mccstat;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH cur_mccstat INTO :h_cat, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_mccstat; FAIL(b); }
grand += h_amt;
if (h_amt > top) { top = h_amt; strncpy(top_cat, h_cat, sizeof(top_cat)-1); }
snprintf(item, sizeof(item), "%s=%ld;", h_cat, h_amt);
if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item);
}
EXEC SQL CLOSE cur_mccstat;
Bchg(b, T_STR1, 0, mix, 0L);
Bchg(b, T_CATEGORY, 0, top_cat, 0L);
setl(b, T_GROSS, grand);
OK(b);
}

View file

@ -0,0 +1,20 @@
/*
* MCC_POLICY.h - au MCC_POLICY (copybook / record header).
* : . : //
* (au_svc.h) ATMI/UBF UBF /tpreturn .
*/
#ifndef MCC_POLICY_H
#define MCC_POLICY_H
#include "au_svc.h"
typedef struct {
char card_no[24];
long v_amount;
long v_count;
long v_result;
} au_mcc_policy_rec_t;
void MCC_POLICY(TPSVCINFO *p);
#endif /* MCC_POLICY_H */

Some files were not shown because too many files have changed in this diff Show more