Phase 1: 실동작 Enduro/X + PostgreSQL XA 매입 슬라이스

- Enduro/X 7.0.12 소스빌드 이미지(-DENABLE_POSTGRES=ON, libndrxxaecpg XA 스위치)
- docker-compose: postgres(max_prepared_transactions=100) + endurox app
- ECPG XA 서비스 3종: ACQUIRE→RECONCILE→SETTLE (tpservice + EXEC SQL)
- 클라이언트 tpbegin/tpcall체인/tpcommit → tmsrv 2PC(prepare/commit) 원자 커밋
- 검증: 서비스 AVAIL, 커밋 row(status=S) psql 확인, 롤백 시 무잔존, pg_prepared_xacts=0
- 규명된 런타임 요건: sysctl fs.mqueue.*, ulimit msgqueue/nofile, XA open-str JSON
- docs/architecture.md, README, service-catalog 설계

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
forge-bot 2026-07-19 09:16:12 +00:00
commit e6e29e6b5d
19 changed files with 1005 additions and 0 deletions

31
app/conf/app.ini Normal file
View file

@ -0,0 +1,31 @@
[@global]
NDRX_CLUSTERISED=1
NDRX_CMDWAIT=1
NDRX_CONFIG=${NDRX_APPHOME}/conf/ndrxconfig.xml
NDRX_DMNLOG=${NDRX_APPHOME}/log/ndrxd.log
NDRX_DPID=${NDRX_APPHOME}/tmp/ndrxd.pid
NDRX_DQMAX=100
NDRX_IPCKEY=44100
NDRX_LDBAL=0
NDRX_LEV=5
NDRX_LOG=${NDRX_APPHOME}/log/xadmin.log
NDRX_MSGMAX=100
NDRX_MSGSIZEMAX=56000
NDRX_NODEID=1
NDRX_QPATH=/dev/mqueue
NDRX_QPREFIX=/acq
NDRX_RNDK=0myWI5nu
NDRX_SRVMAX=10000
NDRX_SVCMAX=20000
NDRX_TOUT=90
NDRX_UBFMAXFLDS=16000
NDRX_LIBEXT=so
NDRX_ULOG=${NDRX_APPHOME}/log
FIELDTBLS=acq.fd
FLDTBLDIR=${NDRX_APPHOME}/ubftab
[@debug]
; * - goes for all binaries not listed below
*= ndrx=5 ubf=1 tp=5 file=
xadmin= ndrx=5 ubf=1 tp=5 file=${NDRX_APPHOME}/log/xadmin.log
ndrxd= ndrx=5 ubf=1 tp=5 file=${NDRX_APPHOME}/log/ndrxd.log

55
app/conf/ndrxconfig.xml Normal file
View file

@ -0,0 +1,55 @@
<?xml version="1.0" ?>
<endurox>
<appconfig>
<sanity>1</sanity>
<brrefresh>5</brrefresh>
<restart_min>1</restart_min>
<restart_step>1</restart_step>
<restart_max>5</restart_max>
<restart_to_check>20</restart_to_check>
<gather_pq_stats>Y</gather_pq_stats>
</appconfig>
<defaults>
<min>1</min>
<max>1</max>
<autokill>1</autokill>
<start_max>20</start_max>
<pingtime>100</pingtime>
<ping_max>800</ping_max>
<end_max>30</end_max>
<killtime>1</killtime>
</defaults>
<servers>
<!-- XA transaction manager for RM1 = the ECPG/PostgreSQL resource.
XA env (NDRX_XA_*) is inherited from the shell (see setapp.sh). -->
<server name="tmsrv">
<srvid>40</srvid>
<min>1</min>
<max>1</max>
<sysopt>-e ${NDRX_APPHOME}/log/tmsrv-rm1.log -r -- -t1 -l${NDRX_APPHOME}/tmlogs/rm1 -m10</sysopt>
</server>
<!-- The three ECPG XA services. Separate binaries so the in-chain
tpcall()s (ACQUIRE->RECONCILE->SETTLE) hit distinct server
processes (avoids single-server self-call deadlock) while sharing
one global transaction across their DB branches. -->
<server name="acqacquire">
<srvid>100</srvid>
<min>1</min>
<max>1</max>
<sysopt>-e ${NDRX_APPHOME}/log/acqacquire.log -r</sysopt>
</server>
<server name="acqreconcile">
<srvid>110</srvid>
<min>1</min>
<max>1</max>
<sysopt>-e ${NDRX_APPHOME}/log/acqreconcile.log -r</sysopt>
</server>
<server name="acqsettle">
<srvid>120</srvid>
<min>1</min>
<max>1</max>
<sysopt>-e ${NDRX_APPHOME}/log/acqsettle.log -r</sysopt>
</server>
</servers>
</endurox>

75
app/entrypoint.sh Executable file
View file

@ -0,0 +1,75 @@
#!/bin/bash
##
## Build + boot the Enduro/X acquiring slice, then stay alive.
## Runs inside the app container (image acquire-x/endurox:7.0.12).
##
set -e
. /app/conf/setapp.sh
echo "=================================================================="
echo " acquire-core-x : build + boot"
echo "=================================================================="
# runtime dirs
mkdir -p /app/log /app/tmp /app/tmlogs/rm1 /app/bin
rm -f /app/log/*.log 2>/dev/null || true
# ensure posix mqueue is available (compose provides the sysctls/ulimits)
if [ ! -d /dev/mqueue ]; then
mkdir -p /dev/mqueue
mount -t mqueue none /dev/mqueue 2>/dev/null || true
fi
# ---------------------------------------------------------------------------
# 1. UBF field header from the field table
# ---------------------------------------------------------------------------
echo "--- mkfldhdr acq.fd ---"
cd /app/ubftab
FIELDTBLS=acq.fd FLDTBLDIR=/app/ubftab mkfldhdr acq.fd
ls -l /app/ubftab/acq.fd.h
# ---------------------------------------------------------------------------
# 2. ECPG precompile + buildserver each .pgc (XA switch loaded via env, so
# buildserver needs no -r; we just link ecpg + pq). buildclient the driver.
# ---------------------------------------------------------------------------
export CFLAGS="-I/app/ubftab -I/app/src -I$(pg_config --includedir)"
PGLIB="-L$(pg_config --libdir) -lecpg -lpq"
cd /app/src
build_svc () {
local src="$1" bin="$2"
echo "--- ecpg $src ---"
ecpg -I/app/ubftab -o "/app/src/${src%.pgc}.c" "/app/src/$src"
echo "--- buildserver -> $bin ---"
buildserver -o "/app/bin/$bin" -f "/app/src/${src%.pgc}.c" -a "$PGLIB"
}
build_svc acquire.pgc acqacquire
build_svc reconcile.pgc acqreconcile
build_svc settle.pgc acqsettle
echo "--- buildclient -> acqdrv ---"
buildclient -o /app/bin/acqdrv -f /app/src/acqdrv.c
ls -l /app/bin
# ---------------------------------------------------------------------------
# 3. Boot Enduro/X
# ---------------------------------------------------------------------------
echo "--- xadmin down/start ---"
xadmin down -y 2>/dev/null || true
xadmin start -y
echo "--- xadmin psc (advertised services) ---"
xadmin psc
echo "--- xadmin ppm (processes) ---"
xadmin ppm
echo "=================================================================="
echo " acquire-core-x : UP. Run the driver with:"
echo " docker compose -f docker/docker-compose.yml exec app /app/run-driver.sh"
echo "=================================================================="
# keep the container (and the ndrxd daemon) alive
tail -F /app/log/ndrxd.log

16
app/run-driver.sh Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
## Run the acquiring driver under a real global XA transaction.
## ./run-driver.sh -> one COMMIT run + one ROLLBACK run (demo)
## ./run-driver.sh <merch> <amount> <bizdate> [abort] -> single custom run
. /app/conf/setapp.sh
if [ "$#" -ge 1 ]; then
exec /app/bin/acqdrv "$@"
fi
echo "===== COMMIT run (ACQUIRE -> RECONCILE -> SETTLE, tpcommit) ====="
/app/bin/acqdrv M0001 1000000 2026-07-19
echo
echo "===== ROLLBACK run (same chain, tpabort) ====="
/app/bin/acqdrv M0002 500000 2026-07-19 abort

86
app/src/acqdrv.c Normal file
View file

@ -0,0 +1,86 @@
/*
* acqdrv - acquiring driver client.
*
* Starts ONE global transaction (tpbegin), fires the ACQUIRE -> RECONCILE ->
* SETTLE chain via a single tpcall("ACQUIRE"), then tpcommit (or tpabort when
* the 4th arg is "abort", to prove XA rollback).
*
* Usage: acqdrv [merchant] [amount] [bizdate YYYY-MM-DD] [abort]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include <ubf.h>
#include "acq.fd.h"
int main(int argc, char **argv)
{
const char *merch = (argc > 1) ? argv[1] : "M0001";
long amount = (argc > 2) ? atol(argv[2]) : 1000000; /* 10,000.00 */
const char *bizdate = (argc > 3) ? argv[3] : "2026-07-19";
int do_abort = (argc > 4 && strcmp(argv[4], "abort") == 0);
UBFH *b;
long rlen = 0;
long pid = 0, fee = 0, net = 0, sid = 0;
char st[16] = "";
BFLDLEN l;
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;
}
b = (UBFH *)tpalloc("UBF", NULL, 4096);
if (b == NULL) {
fprintf(stderr, "tpalloc FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
Bchg(b, T_MERCHANT, 0, (char *)merch, 0L);
Bchg(b, T_AMOUNT, 0, (char *)&amount, 0L);
Bchg(b, T_BIZDATE, 0, (char *)bizdate, 0L);
if (tpbegin(60, 0) < 0) {
fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
if (tpcall("ACQUIRE", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) {
fprintf(stderr, "tpcall(ACQUIRE) FAIL: %s\n", tpstrerror(tperrno));
tpabort(0);
return 1;
}
Bget(b, T_PURCHASE_ID, 0, (char *)&pid, 0L);
Bget(b, T_FEE, 0, (char *)&fee, 0L);
Bget(b, T_NET, 0, (char *)&net, 0L);
Bget(b, T_SETTLE_ID, 0, (char *)&sid, 0L);
l = sizeof(st); Bget(b, T_STATUS, 0, st, &l);
if (do_abort) {
if (tpabort(0) < 0) {
fprintf(stderr, "tpabort FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
printf(">>> ROLLBACK: global tx aborted. purchase_id=%ld should NOT persist "
"(merch=%s amount=%ld)\n", pid, merch, amount);
} else {
if (tpcommit(0) < 0) {
fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno));
tpabort(0);
return 1;
}
printf(">>> COMMIT OK: purchase_id=%ld merch=%s amount=%ld fee=%ld net=%ld "
"settlement_id=%ld status=%s\n", pid, merch, amount, fee, net, sid, st);
}
tpfree((char *)b);
tpclose();
tpterm();
return 0;
}

142
app/src/acquire.pgc Normal file
View file

@ -0,0 +1,142 @@
/*
* ACQUIRE (매입 접수) - ECPG XA service.
*
* Inserts a `purchase` row (fee = amount * 25/10000 MDR, net = amount - fee),
* then within the SAME global transaction tpcall()s RECONCILE.
*
* XA connection is opened by the ECPG XA switch (libndrxxaecpg.so) via tpopen();
* there is deliberately NO `EXEC SQL CONNECT` (matches ref/atmisv67.pgc).
*/
#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"
void ACQUIRE(TPSVCINFO *p);
int tpsvrinit(int argc, char **argv)
{
if (tpopen() < 0) {
userlog("acquire: tpopen FAIL: %s", tpstrerror(tperrno));
return -1;
}
if (tpadvertise("ACQUIRE", ACQUIRE) < 0) {
userlog("acquire: tpadvertise(ACQUIRE) FAIL: %s", tpstrerror(tperrno));
return -1;
}
userlog("acquire: ACQUIRE advertised, RM opened");
return 0;
}
void tpsvrdone(void)
{
tpclose();
userlog("acquire: tpsvrdone");
}
void ACQUIRE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long amount = 0, fee = 0, net = 0;
char merch[64] = "";
char bizdate[16] = "";
BFLDLEN len;
long rlen = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid;
long h_amount;
long h_fee;
long h_net;
char h_merch[64];
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
len = sizeof(merch);
if (Bget(b, T_MERCHANT, 0, merch, &len) < 0) {
userlog("ACQUIRE: missing T_MERCHANT: %s", Bstrerror(Berror));
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
Bget(b, T_AMOUNT, 0, (char *)&amount, 0L);
len = sizeof(bizdate);
Bget(b, T_BIZDATE, 0, bizdate, &len);
fee = amount * 25 / 10000; /* 0.25% MDR */
net = amount - fee;
/* obtain a purchase id (sequence is non-transactional by design) */
EXEC SQL SELECT nextval('purchase_seq') INTO :h_pid;
if (sqlca.sqlcode < 0) {
userlog("ACQUIRE: nextval FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
h_amount = amount;
h_fee = fee;
h_net = net;
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 purchase
(purchase_id, merchant_id, amount, fee, net, status, biz_date)
VALUES (:h_pid, :h_merch, :h_amount, :h_fee, :h_net, 'A', :h_bizdate);
if (sqlca.sqlcode < 0) {
userlog("ACQUIRE: INSERT purchase FAIL [%d] %s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
/* record the approval as well (all under the same global tx) */
EXEC SQL INSERT INTO approval (approval_id, purchase_id, approved)
VALUES (nextval('approval_seq'), :h_pid, true);
if (sqlca.sqlcode < 0) {
userlog("ACQUIRE: INSERT approval FAIL [%d] %s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
Bchg(b, T_PURCHASE_ID, 0, (char *)&h_pid, 0L);
Bchg(b, T_FEE, 0, (char *)&fee, 0L);
Bchg(b, T_NET, 0, (char *)&net, 0L);
Bchg(b, T_STATUS, 0, "A", 0L);
userlog("ACQUIRE pid=%ld merch=%s amount=%ld fee=%ld net=%ld -> RECONCILE",
h_pid, merch, amount, fee, net);
/* Continue the chain under the SAME global transaction. RECONCILE (and, in
* turn, SETTLE) run as separate XA branches (separate DB connections) and
* therefore CANNOT see this not-yet-committed purchase row - so they write
* only their OWN rows (ledger/settlement). This owner branch performs the
* purchase status transition itself once the chain returns, because its
* connection is resumed on the same branch and can see its own INSERT. */
if (tpcall("RECONCILE", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) {
userlog("ACQUIRE: tpcall(RECONCILE) FAIL: %s", tpstrerror(tperrno));
tpreturn(TPFAIL, 0, (char *)b, 0L, 0L);
return;
}
/* branch A resumed here: settle-complete the purchase we own */
EXEC SQL UPDATE purchase
SET status = 'S', updated_at = now()
WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
userlog("ACQUIRE: UPDATE purchase status FAIL [%d] %s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, (char *)b, 0L, 0L);
return;
}
Bchg(b, T_STATUS, 0, "S", 0L);
tpreturn(TPSUCCESS, 0, (char *)b, 0L, 0L);
}
/* vim: set ts=4 sw=4 et smartindent: */

88
app/src/reconcile.pgc Normal file
View file

@ -0,0 +1,88 @@
/*
* RECONCILE (대사) - ECPG XA service.
*
* Marks the purchase matched (status='M'), writes a ledger recon entry, then
* tpcall()s SETTLE - all inside the caller's global transaction.
*/
#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"
void RECONCILE(TPSVCINFO *p);
int tpsvrinit(int argc, char **argv)
{
if (tpopen() < 0) {
userlog("reconcile: tpopen FAIL: %s", tpstrerror(tperrno));
return -1;
}
if (tpadvertise("RECONCILE", RECONCILE) < 0) {
userlog("reconcile: tpadvertise(RECONCILE) FAIL: %s", tpstrerror(tperrno));
return -1;
}
userlog("reconcile: RECONCILE advertised, RM opened");
return 0;
}
void tpsvrdone(void)
{
tpclose();
userlog("reconcile: tpsvrdone");
}
void RECONCILE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long net = 0;
char bizdate[16] = "";
BFLDLEN len;
long rlen = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid;
long h_net;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
if (Bget(b, T_PURCHASE_ID, 0, (char *)&h_pid, 0L) < 0) {
userlog("RECONCILE: missing T_PURCHASE_ID: %s", Bstrerror(Berror));
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
Bget(b, T_NET, 0, (char *)&net, 0L);
len = sizeof(bizdate);
Bget(b, T_BIZDATE, 0, bizdate, &len);
h_net = net;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); h_bizdate[sizeof(h_bizdate) - 1] = 0;
/* This is a distinct XA branch (own DB connection); it writes only its own
* recon evidence row and does NOT touch the purchase row (which belongs to
* the ACQUIRE branch and is not yet committed / not visible here). */
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), :h_pid, 'RECON', :h_net, :h_bizdate);
if (sqlca.sqlcode < 0) {
userlog("RECONCILE: INSERT ledger FAIL [%d] %s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
Bchg(b, T_STATUS, 0, "M", 0L);
userlog("RECONCILE pid=%ld matched -> SETTLE", h_pid);
if (tpcall("SETTLE", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) {
userlog("RECONCILE: tpcall(SETTLE) FAIL: %s", tpstrerror(tperrno));
tpreturn(TPFAIL, 0, (char *)b, 0L, 0L);
return;
}
tpreturn(TPSUCCESS, 0, (char *)b, 0L, 0L);
}
/* vim: set ts=4 sw=4 et smartindent: */

107
app/src/settle.pgc Normal file
View file

@ -0,0 +1,107 @@
/*
* SETTLE (정산) - ECPG XA service.
*
* Inserts a `settlement` (netting) row, writes a ledger settle entry, and marks
* the purchase settled (status='S'). Terminal service of the chain; still runs
* inside the caller's global transaction.
*/
#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"
void SETTLE(TPSVCINFO *p);
int tpsvrinit(int argc, char **argv)
{
if (tpopen() < 0) {
userlog("settle: tpopen FAIL: %s", tpstrerror(tperrno));
return -1;
}
if (tpadvertise("SETTLE", SETTLE) < 0) {
userlog("settle: tpadvertise(SETTLE) FAIL: %s", tpstrerror(tperrno));
return -1;
}
userlog("settle: SETTLE advertised, RM opened");
return 0;
}
void tpsvrdone(void)
{
tpclose();
userlog("settle: tpsvrdone");
}
void SETTLE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long net = 0;
char merch[64] = "";
char bizdate[16] = "";
BFLDLEN len;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid;
long h_sid;
long h_net;
char h_merch[64];
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
if (Bget(b, T_PURCHASE_ID, 0, (char *)&h_pid, 0L) < 0) {
userlog("SETTLE: missing T_PURCHASE_ID: %s", Bstrerror(Berror));
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
Bget(b, T_NET, 0, (char *)&net, 0L);
len = sizeof(merch);
Bget(b, T_MERCHANT, 0, merch, &len);
len = sizeof(bizdate);
Bget(b, T_BIZDATE, 0, bizdate, &len);
h_net = net;
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 nextval('settlement_seq') INTO :h_sid;
if (sqlca.sqlcode < 0) {
userlog("SETTLE: nextval FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
EXEC SQL INSERT INTO settlement
(settlement_id, purchase_id, merchant_id, net, biz_date, status)
VALUES (:h_sid, :h_pid, :h_merch, :h_net, :h_bizdate, 'SETTLED');
if (sqlca.sqlcode < 0) {
userlog("SETTLE: INSERT settlement FAIL [%d] %s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
/* SETTLE is its own XA branch: it writes the settlement + a settle ledger
* entry (its own rows). The purchase status transition is performed by the
* ACQUIRE branch that owns that row. */
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), :h_pid, 'SETTLE', :h_net, :h_bizdate);
if (sqlca.sqlcode < 0) {
userlog("SETTLE: INSERT ledger FAIL [%d] %s",
sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
tpreturn(TPFAIL, 0, p->data, 0L, 0L);
return;
}
Bchg(b, T_SETTLE_ID, 0, (char *)&h_sid, 0L);
Bchg(b, T_STATUS, 0, "S", 0L);
userlog("SETTLE pid=%ld settlement_id=%ld net=%ld status=S", h_pid, h_sid, net);
tpreturn(TPSUCCESS, 0, (char *)b, 0L, 0L);
}
/* vim: set ts=4 sw=4 et smartindent: */

13
app/ubftab/acq.fd Normal file
View file

@ -0,0 +1,13 @@
$/* @(#) Acquiring (카드 매입) UBF field table */
*base 6000
# name id type flag comment
T_MERCHANT 1 string - -
T_AMOUNT 2 long - -
T_FEE 3 long - -
T_NET 4 long - -
T_STATUS 5 string - -
T_PURCHASE_ID 6 long - -
T_BIZDATE 7 string - -
T_SETTLE_ID 8 long - -
T_MSG 9 string - -

12
app/ubftab/acq.fd.h Normal file
View file

@ -0,0 +1,12 @@
/* @(#) Acquiring (카드 매입) UBF field table */
/* fname bfldid */
/* ----- ----- */
#define T_MERCHANT ((BFLDID32)167778161) /* number: 6001 type: string */
#define T_AMOUNT ((BFLDID32)33560434) /* number: 6002 type: long */
#define T_FEE ((BFLDID32)33560435) /* number: 6003 type: long */
#define T_NET ((BFLDID32)33560436) /* number: 6004 type: long */
#define T_STATUS ((BFLDID32)167778165) /* number: 6005 type: string */
#define T_PURCHASE_ID ((BFLDID32)33560438) /* number: 6006 type: long */
#define T_BIZDATE ((BFLDID32)167778167) /* number: 6007 type: string */
#define T_SETTLE_ID ((BFLDID32)33560440) /* number: 6008 type: long */
#define T_MSG ((BFLDID32)167778169) /* number: 6009 type: string */