Phase 2b: 11개 모듈 전량 실동작 (333 서비스, 전서버 부팅)
- 10개 모듈 추가/확장(au py rc st lg cl mm vl mg cm), 각 30 서비스 = 330 + tmsrv - build.sh 자동발견으로 11 모듈서버 + dbio 라이브러리 23 + 배치 22 빌드 - schema.d 11파일 → 75 테이블, 모듈별 disjoint - 부팅 리소스 규명: fs.mqueue.queues_max=8192(330큐>256), NDRX_MSGSIZEMAX 16000· MSGMAX 50(큐당 5.6MB→0.8MB), msgqueue ulimit 2GB → 전 12서버 runok - 검증: xadmin psc 333 AVAIL, 매입체인 XA 커밋, prepared_xacts=0 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8219b85f14
commit
bec83c721e
73 changed files with 10705 additions and 35 deletions
77
app/src/py/batch/py_filegen_batch.pgc
Normal file
77
app/src/py/batch/py_filegen_batch.pgc
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* py_filegen_batch.pgc - py 지급파일 생성 배치 (커서 -> 고정길이 파일 + XA).
|
||||
*
|
||||
* ATMI client: opens the ECPG XA RM, starts ONE global transaction, inserts a
|
||||
* py_file header, then cursors승인(A)된 지급건 for a biz_date writing one
|
||||
* fixed-length record per payout into PAYOUT_<bizdate>.dat and stamping each
|
||||
* payout with the file_id (status 'F') via the dbio layer, then tpcommit drives
|
||||
* XA 2PC. Usage: py_filegen_batch [YYYY-MM-DD]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include "py_dbio.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
|
||||
char path[128], rec[128];
|
||||
FILE *fp;
|
||||
long rows = 0;
|
||||
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16], h_fname[64], h_acct[32];
|
||||
long h_file, h_pay, h_net, h_cnt, h_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
snprintf(h_fname, sizeof(h_fname), "PAYOUT_%s.dat", bizdate);
|
||||
snprintf(path, sizeof(path), "/tmp/%s", h_fname);
|
||||
|
||||
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(net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout WHERE biz_date = :h_bizdate AND status = 'A';
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "COUNT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
EXEC SQL SELECT nextval('py_file_seq') INTO :h_file;
|
||||
if (sqlca.sqlcode < 0) { tpabort(0); return 1; }
|
||||
EXEC SQL INSERT INTO py_file (file_id, biz_date, file_name, record_count, total_amount, status)
|
||||
VALUES (:h_file, :h_bizdate, :h_fname, :h_cnt, :h_sum, 'G');
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FILE INS FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
fp = fopen(path, "w");
|
||||
if (fp == NULL) { fprintf(stderr, "fopen FAIL: %s\n", path); tpabort(0); return 1; }
|
||||
|
||||
EXEC SQL DECLARE fgc CURSOR FOR
|
||||
SELECT payout_id, account_no, net FROM py_payout
|
||||
WHERE biz_date = :h_bizdate AND status = 'A'
|
||||
ORDER BY payout_id;
|
||||
EXEC SQL OPEN fgc;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); fclose(fp); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH fgc INTO :h_pay, :h_acct, :h_net;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE fgc; fclose(fp); tpabort(0); return 1; }
|
||||
/* 고정길이 레코드: payout_id(12) account_no(20) net(15) = 47 bytes + NL */
|
||||
snprintf(rec, sizeof(rec), "%012ld%-20.20s%015ld\n", h_pay, h_acct, h_net);
|
||||
fputs(rec, fp);
|
||||
if (pydb_set_file(h_pay, h_file, "F") < 0) {
|
||||
EXEC SQL CLOSE fgc; fclose(fp); tpabort(0); return 1;
|
||||
}
|
||||
rows++;
|
||||
}
|
||||
EXEC SQL CLOSE fgc;
|
||||
fclose(fp);
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_filegen_batch COMMIT: file=%s file_id=%ld records=%ld total=%ld\n",
|
||||
path, h_file, rows, h_sum);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
52
app/src/py/batch/py_result_batch.pgc
Normal file
52
app/src/py/batch/py_result_batch.pgc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* py_result_batch.pgc - py 지급결과 반영 배치 (커서 F->P + 집계 + XA).
|
||||
*
|
||||
* Cursors over payouts that were filed (status='F') for a biz_date, flips each
|
||||
* to 지급완료(status='P') and rolls a per-merchant payout summary via the dbio
|
||||
* layer - all under ONE global XA transaction. Usage: py_result_batch [YYYY-MM-DD]
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include "py_dbio.h"
|
||||
|
||||
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_merch[64];
|
||||
long h_pay, h_net;
|
||||
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 prc CURSOR FOR
|
||||
SELECT payout_id, merchant_id, net FROM py_payout
|
||||
WHERE biz_date = :h_bizdate AND status = 'F'
|
||||
ORDER BY payout_id;
|
||||
EXEC SQL OPEN prc;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
|
||||
|
||||
for (;;) {
|
||||
EXEC SQL FETCH prc INTO :h_pay, :h_merch, :h_net;
|
||||
if (sqlca.sqlcode == 100) break;
|
||||
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE prc; tpabort(0); return 1; }
|
||||
if (pydb_update_status(h_pay, "P") < 0) { EXEC SQL CLOSE prc; tpabort(0); return 1; }
|
||||
if (pydb_upsert_summary(h_merch, bizdate, 1, h_net) < 0) { EXEC SQL CLOSE prc; tpabort(0); return 1; }
|
||||
rows++;
|
||||
}
|
||||
EXEC SQL CLOSE prc;
|
||||
|
||||
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
|
||||
printf(">>> py_result_batch COMMIT: bizdate=%s paid(F->P)=%ld\n", bizdate, rows);
|
||||
|
||||
tpclose(); tpterm();
|
||||
return 0;
|
||||
}
|
||||
44
app/src/py/dbio/py_account_dbio.pgc
Normal file
44
app/src/py/dbio/py_account_dbio.pgc
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* py_account_dbio.pgc - 지급계좌(py_account) DB 접근 (libpydbio.a).
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <userlog.h>
|
||||
#include "py_dbio.h"
|
||||
|
||||
int pydb_account_check(const char *acct, char *status_out, long *balance_out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_bal;
|
||||
char h_acct[32], h_status[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_acct, acct, sizeof(h_acct) - 1); h_acct[sizeof(h_acct)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT status, balance INTO :h_status, :h_bal
|
||||
FROM py_account WHERE account_no = :h_acct;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("pydb_account_check FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
strncpy(status_out, h_status, 7); status_out[7] = 0;
|
||||
*balance_out = h_bal;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pydb_account_add(const char *acct, long delta)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_delta = delta;
|
||||
char h_acct[32];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_acct, acct, sizeof(h_acct) - 1); h_acct[sizeof(h_acct)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE py_account SET balance = balance + :h_delta, updated_at = now()
|
||||
WHERE account_no = :h_acct;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("pydb_account_add FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
26
app/src/py/dbio/py_dbio.h
Normal file
26
app/src/py/dbio/py_dbio.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* py_dbio.h - py 모듈 (지급) DB 접근 계층 (libpydbio.a).
|
||||
* ECPG (EXEC SQL) functions compiled into a static library and linked into
|
||||
* py_svr and the py batches. They run on the caller's XA branch/connection
|
||||
* (no EXEC SQL CONNECT); sqlca is the shared per-thread ECPG SQLCA.
|
||||
* Return convention: 0 = ok, -1 = SQL error (caller may also inspect sqlca).
|
||||
*/
|
||||
#ifndef PY_DBIO_H
|
||||
#define PY_DBIO_H
|
||||
|
||||
/* payout */
|
||||
long pydb_next_payout_id(void);
|
||||
int pydb_insert_payout(long payout_id, const char *merch, const char *acct, long amount,
|
||||
long fee, long net, const char *status, const char *channel,
|
||||
const char *bizdate);
|
||||
int pydb_update_status(long payout_id, const char *status);
|
||||
int pydb_get_status(long payout_id, char *out /* >= 2 bytes */);
|
||||
int pydb_set_file(long payout_id, long file_id, const char *status);
|
||||
int pydb_upsert_summary(const char *merch, const char *bizdate, long cnt, long amount);
|
||||
long pydb_insert_adjust(long payout_id, long delta, const char *reason, const char *bizdate);
|
||||
|
||||
/* account */
|
||||
int pydb_account_check(const char *acct, char *status_out /* >= 8 */, long *balance_out);
|
||||
int pydb_account_add(const char *acct, long delta);
|
||||
|
||||
#endif /* PY_DBIO_H */
|
||||
144
app/src/py/dbio/py_payout_dbio.pgc
Normal file
144
app/src/py/dbio/py_payout_dbio.pgc
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* py_payout_dbio.pgc - 지급(payout)/조정/집계 DB 접근 (libpydbio.a).
|
||||
* ECPG functions; run on the caller's XA branch. No EXEC SQL CONNECT.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <userlog.h>
|
||||
#include "py_dbio.h"
|
||||
|
||||
long pydb_next_payout_id(void)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT nextval('py_payout_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("pydb_next_payout_id FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
|
||||
int pydb_insert_payout(long payout_id, const char *merch, const char *acct, long amount,
|
||||
long fee, long net, const char *status, const char *channel,
|
||||
const char *bizdate)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = payout_id, h_amount = amount, h_fee = fee, h_net = net;
|
||||
char h_merch[64], h_acct[32], h_status[8], h_channel[16], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_acct, acct, sizeof(h_acct) - 1); h_acct[sizeof(h_acct)-1] = 0;
|
||||
strncpy(h_status, status, sizeof(h_status) - 1); h_status[sizeof(h_status)-1] = 0;
|
||||
strncpy(h_channel, channel, sizeof(h_channel) - 1); h_channel[sizeof(h_channel)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO py_payout
|
||||
(payout_id, merchant_id, account_no, amount, fee, net, status, channel, biz_date)
|
||||
VALUES (:h_id, :h_merch, :h_acct, :h_amount, :h_fee, :h_net, :h_status, :h_channel, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("pydb_insert_payout FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pydb_update_status(long payout_id, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = payout_id;
|
||||
char h_status[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_status, status, sizeof(h_status) - 1); h_status[sizeof(h_status)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE py_payout SET status = :h_status, updated_at = now()
|
||||
WHERE payout_id = :h_id;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("pydb_update_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pydb_get_status(long payout_id, char *out)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = payout_id;
|
||||
char h_status[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
EXEC SQL SELECT status INTO :h_status FROM py_payout WHERE payout_id = :h_id;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("pydb_get_status FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
out[0] = h_status[0];
|
||||
out[1] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pydb_set_file(long payout_id, long file_id, const char *status)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id = payout_id, h_file = file_id;
|
||||
char h_status[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_status, status, sizeof(h_status) - 1); h_status[sizeof(h_status)-1] = 0;
|
||||
|
||||
EXEC SQL UPDATE py_payout SET file_id = :h_file, status = :h_status, updated_at = now()
|
||||
WHERE payout_id = :h_id;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
|
||||
userlog("pydb_set_file FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pydb_upsert_summary(const char *merch, const char *bizdate, long cnt, long amount)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt = cnt, h_amt = amount;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL INSERT INTO py_summary (merchant_id, biz_date, payout_count, payout_amount)
|
||||
VALUES (:h_merch, :h_bizdate, :h_cnt, :h_amt)
|
||||
ON CONFLICT (merchant_id, biz_date) DO UPDATE
|
||||
SET payout_count = py_summary.payout_count + EXCLUDED.payout_count,
|
||||
payout_amount = py_summary.payout_amount + EXCLUDED.payout_amount,
|
||||
updated_at = now();
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("pydb_upsert_summary FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long pydb_insert_adjust(long payout_id, long delta, const char *reason, const char *bizdate)
|
||||
{
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id, h_pay = payout_id, h_delta = delta;
|
||||
char h_reason[128], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
strncpy(h_reason, reason, sizeof(h_reason) - 1); h_reason[sizeof(h_reason)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT nextval('py_adjust_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) return -1;
|
||||
|
||||
EXEC SQL INSERT INTO py_adjust (adj_id, payout_id, delta, reason, biz_date)
|
||||
VALUES (:h_id, :h_pay, :h_delta, :h_reason, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) {
|
||||
userlog("pydb_insert_adjust FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
|
||||
return -1;
|
||||
}
|
||||
return h_id;
|
||||
}
|
||||
621
app/src/py/py_svr.pgc
Normal file
621
app/src/py/py_svr.pgc
Normal file
|
|
@ -0,0 +1,621 @@
|
|||
/*
|
||||
* py_svr.pgc - py (지급) MODULE SERVER.
|
||||
*
|
||||
* ONE binary that tpadvertise()s ALL ~30 online py services (the real Tuxedo
|
||||
* "one module server, many services" pattern). Every service is a distinct
|
||||
* 지급 operation with genuine EXEC SQL (directly, or via the linked libpydbio.a
|
||||
* dbio layer) and common math from libacqcommon.a.
|
||||
*
|
||||
* XA: the connection is opened once by tpopen() (ECPG XA switch); there is NO
|
||||
* EXEC SQL CONNECT. Owner-writes discipline: services in this server never
|
||||
* tpcall each other; each owns its tables on this process's XA branch.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <atmi.h>
|
||||
#include <ubf.h>
|
||||
#include <userlog.h>
|
||||
#include <ndebug.h>
|
||||
#include "acq.fd.h"
|
||||
#include "py_dbio.h"
|
||||
#include "acq_common.h"
|
||||
|
||||
/* ----- small UBF helpers ------------------------------------------------- */
|
||||
static long getl(UBFH *b, BFLDID f) { long v = 0; Bget(b, f, 0, (char *)&v, 0L); return v; }
|
||||
static void gets_(UBFH *b, BFLDID f, char *out, int cap)
|
||||
{ BFLDLEN l = cap; out[0] = 0; Bget(b, f, 0, out, &l); }
|
||||
static void setl(UBFH *b, BFLDID f, long v) { Bchg(b, f, 0, (char *)&v, 0L); }
|
||||
|
||||
#define FAIL(b) do { tpreturn(TPFAIL, 0, (char *)(b), 0L, 0L); return; } while (0)
|
||||
#define OK(b) do { tpreturn(TPSUCCESS, 0, (char *)(b), 0L, 0L); return; } while (0)
|
||||
|
||||
/* ----- service prototypes ------------------------------------------------ */
|
||||
void PAY_FILEGEN(TPSVCINFO *p); void PAY_RESULT(TPSVCINFO *p);
|
||||
void PAY_REPROC(TPSVCINFO *p); void PAY_HOLD(TPSVCINFO *p);
|
||||
void PAY_RELEASE(TPSVCINFO *p); void PAY_CANCEL(TPSVCINFO *p);
|
||||
void PAY_ACCTCHK(TPSVCINFO *p); void PAY_SPLIT(TPSVCINFO *p);
|
||||
void PAY_INT(TPSVCINFO *p); void PAY_XFERLINK(TPSVCINFO *p);
|
||||
void PAY_STATUS(TPSVCINFO *p); void PAY_SCHEDULE(TPSVCINFO *p);
|
||||
void PAY_MERCHSUM(TPSVCINFO *p); void PAY_NETOFF(TPSVCINFO *p);
|
||||
void PAY_ADVANCE(TPSVCINFO *p); void PAY_RETRY(TPSVCINFO *p);
|
||||
void PAY_APPROVE(TPSVCINFO *p); void PAY_REJECT(TPSVCINFO *p);
|
||||
void PAY_RECON(TPSVCINFO *p); void PAY_STATEMENT(TPSVCINFO *p);
|
||||
void PAY_FEEDED(TPSVCINFO *p); void PAY_ADJUST(TPSVCINFO *p);
|
||||
void PAY_BULK(TPSVCINFO *p); void PAY_INQUIRY(TPSVCINFO *p);
|
||||
void PAY_HISTORY(TPSVCINFO *p); void PAY_REVERSE(TPSVCINFO *p);
|
||||
void PAY_CONFIRM(TPSVCINFO *p); void PAY_REMIT(TPSVCINFO *p);
|
||||
void PAY_CLOSE(TPSVCINFO *p); void PAY_SUMMARY(TPSVCINFO *p);
|
||||
|
||||
/* ----- advertise table (this is what "scales to 30" cleanly) ------------- */
|
||||
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
|
||||
{"PAY_FILEGEN", PAY_FILEGEN}, {"PAY_RESULT", PAY_RESULT},
|
||||
{"PAY_REPROC", PAY_REPROC}, {"PAY_HOLD", PAY_HOLD},
|
||||
{"PAY_RELEASE", PAY_RELEASE}, {"PAY_CANCEL", PAY_CANCEL},
|
||||
{"PAY_ACCTCHK", PAY_ACCTCHK}, {"PAY_SPLIT", PAY_SPLIT},
|
||||
{"PAY_INT", PAY_INT}, {"PAY_XFERLINK", PAY_XFERLINK},
|
||||
{"PAY_STATUS", PAY_STATUS}, {"PAY_SCHEDULE", PAY_SCHEDULE},
|
||||
{"PAY_MERCHSUM", PAY_MERCHSUM}, {"PAY_NETOFF", PAY_NETOFF},
|
||||
{"PAY_ADVANCE", PAY_ADVANCE}, {"PAY_RETRY", PAY_RETRY},
|
||||
{"PAY_APPROVE", PAY_APPROVE}, {"PAY_REJECT", PAY_REJECT},
|
||||
{"PAY_RECON", PAY_RECON}, {"PAY_STATEMENT", PAY_STATEMENT},
|
||||
{"PAY_FEEDED", PAY_FEEDED}, {"PAY_ADJUST", PAY_ADJUST},
|
||||
{"PAY_BULK", PAY_BULK}, {"PAY_INQUIRY", PAY_INQUIRY},
|
||||
{"PAY_HISTORY", PAY_HISTORY}, {"PAY_REVERSE", PAY_REVERSE},
|
||||
{"PAY_CONFIRM", PAY_CONFIRM}, {"PAY_REMIT", PAY_REMIT},
|
||||
{"PAY_CLOSE", PAY_CLOSE}, {"PAY_SUMMARY", PAY_SUMMARY},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int tpsvrinit(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
if (tpopen() < 0) {
|
||||
userlog("py_svr: tpopen FAIL: %s", tpstrerror(tperrno));
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; SVCS[i].name != NULL; i++) {
|
||||
if (tpadvertise((char *)SVCS[i].name, SVCS[i].fn) < 0) {
|
||||
userlog("py_svr: tpadvertise(%s) FAIL: %s", SVCS[i].name, tpstrerror(tperrno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
userlog("py_svr: %d py(지급) 서비스 광고 완료, RM 오픈", i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tpsvrdone(void)
|
||||
{
|
||||
tpclose();
|
||||
userlog("py_svr: tpsvrdone");
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
/* 1. PAY_FILEGEN - 지급 파일 헤더 생성: 당일 승인(A) 지급건 집계 -> py_file. */
|
||||
/* ========================================================================= */
|
||||
void PAY_FILEGEN(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bizdate[16], fname[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id, h_cnt, h_sum;
|
||||
char h_bizdate[16], h_fname[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout WHERE biz_date = :h_bizdate AND status = 'A';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
snprintf(fname, sizeof(fname), "PAYOUT_%s.dat", bizdate);
|
||||
strncpy(h_fname, fname, sizeof(h_fname)-1); h_fname[sizeof(h_fname)-1] = 0;
|
||||
EXEC SQL SELECT nextval('py_file_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL INSERT INTO py_file (file_id, biz_date, file_name, record_count, total_amount, status)
|
||||
VALUES (:h_id, :h_bizdate, :h_fname, :h_cnt, :h_sum, 'G');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_ID2, h_id);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
Bchg(b, T_STR1, 0, fname, 0L);
|
||||
userlog("PAY_FILEGEN file_id=%ld name=%s recs=%ld amt=%ld", h_id, fname, h_cnt, h_sum);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 2. PAY_RESULT - 지급 결과 반영: 지급건 상태 P(지급완료). */
|
||||
void PAY_RESULT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long payout_id = getl(b, T_ID1);
|
||||
if (pydb_update_status(payout_id, "P") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "P", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 3. PAY_REPROC - 재처리: 상태 R(요청)으로 되돌림. */
|
||||
void PAY_REPROC(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long payout_id = getl(b, T_ID1);
|
||||
if (pydb_update_status(payout_id, "R") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "R", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 4. PAY_HOLD - 지급 보류. */
|
||||
void PAY_HOLD(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long payout_id = getl(b, T_ID1);
|
||||
if (pydb_update_status(payout_id, "H") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "H", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 5. PAY_RELEASE - 보류 해제(요청 상태로). */
|
||||
void PAY_RELEASE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long payout_id = getl(b, T_ID1);
|
||||
if (pydb_update_status(payout_id, "R") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "R", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 6. PAY_CANCEL - 지급 취소. */
|
||||
void PAY_CANCEL(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bizdate[16], reason[128];
|
||||
long payout_id, amt, adj;
|
||||
payout_id = getl(b, T_ID1);
|
||||
amt = getl(b, T_AMOUNT);
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "CANCEL");
|
||||
adj = pydb_insert_adjust(payout_id, -amt, reason, bizdate);
|
||||
if (adj < 0) FAIL(b);
|
||||
if (pydb_update_status(payout_id, "C") < 0) FAIL(b);
|
||||
setl(b, T_ADJ, adj);
|
||||
Bchg(b, T_STATUS, 0, "C", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 7. PAY_ACCTCHK - 지급계좌 유효성 점검 (dbio). */
|
||||
void PAY_ACCTCHK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char acct[32], st[8];
|
||||
long bal = 0;
|
||||
gets_(b, T_KEY1, acct, sizeof(acct));
|
||||
if (pydb_account_check(acct, st, &bal) < 0) { setl(b, T_RC, 1); FAIL(b); }
|
||||
setl(b, T_AMT1, bal);
|
||||
Bchg(b, T_STR1, 0, st, 0L);
|
||||
setl(b, T_RC, (strcmp(st, "ACTIVE") == 0) ? 0 : 1);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 8. PAY_SPLIT - 지급 분할: 금액을 N건으로 나눠 지급건 생성. */
|
||||
void PAY_SPLIT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], acct[32], bizdate[16];
|
||||
long n, amount, i, part, payout_id;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_KEY1, acct, sizeof(acct));
|
||||
amount = getl(b, T_AMOUNT);
|
||||
n = getl(b, T_COUNT); if (n < 1) n = 2;
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
part = amount / n;
|
||||
for (i = 0; i < n; i++) {
|
||||
payout_id = pydb_next_payout_id();
|
||||
if (payout_id < 0) FAIL(b);
|
||||
if (pydb_insert_payout(payout_id, merch, acct, part, 0, part, "R", "SPLIT", bizdate) < 0) FAIL(b);
|
||||
}
|
||||
setl(b, T_COUNT, n);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 9. PAY_INT - 선지급 이자 계산 (일할, bps). */
|
||||
void PAY_INT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long principal, days, bps, interest;
|
||||
principal = getl(b, T_AMOUNT);
|
||||
days = getl(b, T_ARG1); if (days < 1) days = 1;
|
||||
bps = getl(b, T_ARG2); if (bps <= 0) bps = 15; /* 0.15%/일 */
|
||||
interest = principal * bps * days / 10000;
|
||||
setl(b, T_AMT1, interest);
|
||||
setl(b, T_NET, principal - interest);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 10. PAY_XFERLINK - 이체 연계 원장 기록(py_ledger). */
|
||||
void PAY_XFERLINK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pay, h_amt;
|
||||
char h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1); h_amt = getl(b, T_AMOUNT);
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date)
|
||||
VALUES (nextval('py_ledger_seq'), :h_pay, 'XFER', :h_amt, :h_bizdate);
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 11. PAY_STATUS - 지급 상태 조회 (dbio). */
|
||||
void PAY_STATUS(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long payout_id = getl(b, T_ID1);
|
||||
char st[4];
|
||||
if (pydb_get_status(payout_id, st) < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, st, 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 12. PAY_SCHEDULE - 지급 예약 등록(py_schedule). */
|
||||
void PAY_SCHEDULE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char rundate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_id, h_pay;
|
||||
char h_rundate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1);
|
||||
gets_(b, T_BIZDATE, rundate, sizeof(rundate));
|
||||
strncpy(h_rundate, rundate, sizeof(h_rundate)-1); h_rundate[sizeof(h_rundate)-1] = 0;
|
||||
EXEC SQL SELECT nextval('py_schedule_seq') INTO :h_id;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
EXEC SQL INSERT INTO py_schedule (schedule_id, payout_id, run_date, status)
|
||||
VALUES (:h_id, :h_pay, :h_rundate, 'S');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_ID2, h_id);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 13. PAY_MERCHSUM - 가맹점 당일 지급 집계 upsert (dbio). */
|
||||
void PAY_MERCHSUM(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt, h_amt;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_amt
|
||||
FROM py_payout WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (pydb_upsert_summary(merch, bizdate, h_cnt, h_amt) < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_amt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 14. PAY_NETOFF - 상계(net-off): 지급 금액에서 차감액 반영. */
|
||||
void PAY_NETOFF(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pay, h_off, h_new;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1);
|
||||
h_off = getl(b, T_AMOUNT);
|
||||
EXEC SQL SELECT net INTO :h_new FROM py_payout WHERE payout_id = :h_pay;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
h_new = h_new - h_off;
|
||||
EXEC SQL UPDATE py_payout SET net = :h_new, updated_at = now() WHERE payout_id = :h_pay;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_NET, h_new);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 15. PAY_ADVANCE - 선지급(advance) 지급건 생성. */
|
||||
void PAY_ADVANCE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], acct[32], bizdate[16];
|
||||
long amount, fee, net, payout_id;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_KEY1, acct, sizeof(acct));
|
||||
amount = getl(b, T_AMOUNT);
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
fee = acq_fee(amount, 100); /* 선지급 수수료 1% */
|
||||
net = amount - fee;
|
||||
payout_id = pydb_next_payout_id();
|
||||
if (payout_id < 0) FAIL(b);
|
||||
if (pydb_insert_payout(payout_id, merch, acct, amount, fee, net, "A", "ADVANCE", bizdate) < 0) FAIL(b);
|
||||
setl(b, T_ID1, payout_id);
|
||||
setl(b, T_FEE, fee);
|
||||
setl(b, T_NET, net);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 16. PAY_RETRY - 실패 지급 재시도(J/H -> R). */
|
||||
void PAY_RETRY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pay;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1);
|
||||
EXEC SQL UPDATE py_payout SET status = 'R', retry_count = retry_count + 1, updated_at = now()
|
||||
WHERE payout_id = :h_pay AND status IN ('J', 'H');
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); FAIL(b); }
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_STATUS, 0, "R", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 17. PAY_APPROVE - 지급 승인. */
|
||||
void PAY_APPROVE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long payout_id = getl(b, T_ID1);
|
||||
if (pydb_update_status(payout_id, "A") < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "A", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 18. PAY_REJECT - 지급 반려 + 사유 기록. */
|
||||
void PAY_REJECT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char reason[128];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pay;
|
||||
char h_reason[128];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1);
|
||||
gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "REJECT");
|
||||
strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0;
|
||||
EXEC SQL UPDATE py_payout SET status = 'J', reject_reason = :h_reason, updated_at = now()
|
||||
WHERE payout_id = :h_pay;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "J", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 19. PAY_RECON - 지급-파일 대사: 파일에 편성된 건수/합계 확인. */
|
||||
void PAY_RECON(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_file, h_cnt, h_sum;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_file = getl(b, T_ID2);
|
||||
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum
|
||||
FROM py_payout WHERE file_id = :h_file;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_sum);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 20. PAY_STATEMENT - 가맹점 지급 명세: 기간(당일) 지급 합계 집계. */
|
||||
void PAY_STATEMENT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt, h_amt, h_fee, h_net;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0), coalesce(sum(net),0)
|
||||
INTO :h_cnt, :h_amt, :h_fee, :h_net FROM py_payout
|
||||
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'P';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_amt);
|
||||
setl(b, T_FEE, h_fee);
|
||||
setl(b, T_NET, h_net);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 21. PAY_FEEDED - 수수료 공제(fee deduction): net 재계산. */
|
||||
void PAY_FEEDED(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
long amount, bps, fee, net;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pay, h_fee, h_net;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1);
|
||||
amount = getl(b, T_AMOUNT);
|
||||
bps = getl(b, T_ARG1); if (bps <= 0) bps = 30;
|
||||
fee = acq_fee(amount, bps); net = amount - fee;
|
||||
h_fee = fee; h_net = net;
|
||||
EXEC SQL UPDATE py_payout SET fee = :h_fee, net = :h_net, updated_at = now()
|
||||
WHERE payout_id = :h_pay;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
setl(b, T_FEE, fee); setl(b, T_NET, net);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 22. PAY_ADJUST - 지급 조정 기록 + net 반영 (dbio + 직접 SQL). */
|
||||
void PAY_ADJUST(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bizdate[16], reason[128];
|
||||
long payout_id, delta, adj;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long e_pay, e_delta;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
payout_id = getl(b, T_ID1);
|
||||
delta = getl(b, T_DELTA);
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "ADJ");
|
||||
adj = pydb_insert_adjust(payout_id, delta, reason, bizdate);
|
||||
if (adj < 0) FAIL(b);
|
||||
e_pay = payout_id; e_delta = delta;
|
||||
EXEC SQL UPDATE py_payout SET net = net + :e_delta, updated_at = now()
|
||||
WHERE payout_id = :e_pay;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
setl(b, T_ADJ, adj);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 23. PAY_BULK - 대량 지급 생성: N건 반복 등록. */
|
||||
void PAY_BULK(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], acct[32], bizdate[16];
|
||||
long n, amount, i, payout_id;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_KEY1, acct, sizeof(acct));
|
||||
amount = getl(b, T_AMOUNT);
|
||||
n = getl(b, T_COUNT); if (n < 1) n = 1;
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
for (i = 0; i < n; i++) {
|
||||
payout_id = pydb_next_payout_id();
|
||||
if (payout_id < 0) FAIL(b);
|
||||
if (pydb_insert_payout(payout_id, merch, acct, amount, 0, amount, "R", "BULK", bizdate) < 0) FAIL(b);
|
||||
}
|
||||
setl(b, T_COUNT, n);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 24. PAY_INQUIRY - 지급 단건 상세 조회. */
|
||||
void PAY_INQUIRY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], acct[32], st[8];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_pay, h_amt, h_fee, h_net;
|
||||
char h_merch[64], h_acct[32], h_status[8];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_pay = getl(b, T_ID1);
|
||||
EXEC SQL SELECT merchant_id, account_no, amount, fee, net, status
|
||||
INTO :h_merch, :h_acct, :h_amt, :h_fee, :h_net, :h_status
|
||||
FROM py_payout WHERE payout_id = :h_pay;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
strncpy(merch, h_merch, sizeof(merch)-1); merch[sizeof(merch)-1] = 0;
|
||||
strncpy(acct, h_acct, sizeof(acct)-1); acct[sizeof(acct)-1] = 0;
|
||||
strncpy(st, h_status, sizeof(st)-1); st[sizeof(st)-1] = 0;
|
||||
Bchg(b, T_MERCHANT, 0, merch, 0L);
|
||||
Bchg(b, T_KEY1, 0, acct, 0L);
|
||||
Bchg(b, T_STATUS, 0, st, 0L);
|
||||
setl(b, T_AMOUNT, h_amt);
|
||||
setl(b, T_FEE, h_fee);
|
||||
setl(b, T_NET, h_net);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 25. PAY_HISTORY - 가맹점 지급 이력 건수 조회. */
|
||||
void PAY_HISTORY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt;
|
||||
char h_merch[64];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
EXEC SQL SELECT count(*) INTO :h_cnt FROM py_payout WHERE merchant_id = :h_merch;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 26. PAY_REVERSE - 지급 역분개(reverse) + 계좌 잔액 원복. */
|
||||
void PAY_REVERSE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char acct[32];
|
||||
long payout_id = getl(b, T_ID1), amount = getl(b, T_AMOUNT);
|
||||
gets_(b, T_KEY1, acct, sizeof(acct));
|
||||
if (pydb_update_status(payout_id, "V") < 0) FAIL(b);
|
||||
if (acct[0] && pydb_account_add(acct, amount) < 0) FAIL(b);
|
||||
Bchg(b, T_STATUS, 0, "V", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 27. PAY_CONFIRM - 지급 확정(confirm): 파일 상태 C(확정). */
|
||||
void PAY_CONFIRM(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_file;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
h_file = getl(b, T_ID2);
|
||||
EXEC SQL UPDATE py_file SET status = 'C', confirmed_at = now() WHERE file_id = :h_file;
|
||||
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 28. PAY_REMIT - 실지급 송금: 상태 P + 계좌 잔액 차감. */
|
||||
void PAY_REMIT(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char acct[32], st[8];
|
||||
long payout_id, net, bal = 0;
|
||||
payout_id = getl(b, T_ID1);
|
||||
net = getl(b, T_NET);
|
||||
gets_(b, T_KEY1, acct, sizeof(acct));
|
||||
if (pydb_account_check(acct, st, &bal) < 0) FAIL(b);
|
||||
if (bal < net) { setl(b, T_RC, 1); FAIL(b); }
|
||||
if (pydb_account_add(acct, -net) < 0) FAIL(b);
|
||||
if (pydb_update_status(payout_id, "P") < 0) FAIL(b);
|
||||
setl(b, T_RC, 0);
|
||||
Bchg(b, T_STATUS, 0, "P", 0L);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 29. PAY_CLOSE - 일마감: 당일 지급완료(P)건 상태 X(마감)로 일괄 전환. */
|
||||
void PAY_CLOSE(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
char h_bizdate[16];
|
||||
long h_cnt;
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
EXEC SQL UPDATE py_payout SET status = 'X', updated_at = now()
|
||||
WHERE biz_date = :h_bizdate AND status = 'P';
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
h_cnt = sqlca.sqlerrd[2];
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* 30. PAY_SUMMARY - 가맹점 일별 지급 집계 조회(py_summary). */
|
||||
void PAY_SUMMARY(TPSVCINFO *p)
|
||||
{
|
||||
UBFH *b = (UBFH *)p->data;
|
||||
char merch[64], bizdate[16];
|
||||
EXEC SQL BEGIN DECLARE SECTION;
|
||||
long h_cnt, h_amt;
|
||||
char h_merch[64], h_bizdate[16];
|
||||
EXEC SQL END DECLARE SECTION;
|
||||
gets_(b, T_MERCHANT, merch, sizeof(merch));
|
||||
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
|
||||
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
|
||||
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
|
||||
EXEC SQL SELECT coalesce(payout_count,0), coalesce(payout_amount,0) INTO :h_cnt, :h_amt
|
||||
FROM py_summary WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
|
||||
if (sqlca.sqlcode < 0) FAIL(b);
|
||||
if (sqlca.sqlcode == 100) { h_cnt = 0; h_amt = 0; }
|
||||
setl(b, T_COUNT, h_cnt);
|
||||
setl(b, T_GROSS, h_amt);
|
||||
OK(b);
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 et smartindent: */
|
||||
Loading…
Add table
Add a link
Reference in a new issue