Wave E/F: 업무흐름 tpcall 오케스트레이션 + 크로스브랜치 교착 제거

477개 서비스를 tpcall 체인으로 연결하고(2 → 477), 그 과정에서 드러난
런타임 결함들을 실행 검증으로 잡아냈다.

체인 오케스트레이션 (Wave E)
- 매입/승인/정산/지급/대사/원장/마감/마스터/전문 흐름을 모듈 간 tpcall 로 연결
- 호출그래프 553노드 494엣지, 순환 0, SELFCALL 0
- 핵심 경로: ACQUIRE→RECONCILE→SETTLE→ST_MDR→LG_SETTLEPOST (6단, XA 2PC)

런타임 결함 수정 (Wave F) — 정적 게이트로는 잡히지 않던 것들
1. 모듈내 체인 자기교착: 모듈 서버 카피가 1이면 SETTLE→ST_MDR 처럼 같은
   서버로 되돌아오는 호출이 자기 요청을 못 꺼내 TPETIME. 카피 4로 상향.
2. 크로스브랜치 핫로우 교착: 한 글로벌 트랜잭션의 형제 XA 브랜치가 같은 행을
   UPDATE 하면 같은 트랜잭션이라 락이 안 풀려 타임아웃까지 블록된다.
   - mg_channel: 비소유자 96개 서비스의 UPDATE 제거, STAN 은 시퀀스 채번으로
     (실제 전문 스위치 방식). 생애주기 소유자 7개만 UPDATE 유지.
   - lg_merch_bal: ACQ_SETTLELINK 의 LG_SETTLEPOST 다이아몬드 호출 제거.
     교착일 뿐 아니라 같은 정산건이 원장에 이중 전기되는 회계 오류였다.
   - cl_close_log / py_payout / lg_account_bal: CHAIN 마커로 쓰기 소유자 일원화.
     py_payout 건은 피호출자가 호출자의 미커밋 승인을 볼 수 없어 기능적으로도
     동작 불능이었다.
3. cm_log PK 충돌: st 131개 파일이 max(log_id)+1 로 채번해 nextval 을 쓰는
   피호출자와 동일 PK 를 INSERT, 미커밋 인덱스 엔트리에 걸려 블록. 전부
   nextval('cm_log_seq') 로 전환.
4. strncpy 스택 파괴: cmdb_get_param 이 strncpy(out, h_val, 127) 로 항상 128
   바이트를 써서, bizdate[16] 을 넘기는 호출부 133곳이 매번 호출자 스택 112
   바이트를 훼손했다. 인접 ecpg 호스트 변수가 오염돼 서버 카피가 세그폴트.
   DB 락 대기가 0인데 60초 블록되는 증상의 원인. cm_code/py_payout_reject 동일.
5. ACQ_CANCEL→LIMIT_RST 입력계약 불일치: 카드번호를 안 실어 보내 항상 거절.
   승인원장에서 카드를 끌어오고, 무승인 매입은 한도원복을 생략하도록 수정.
6. mgdb_channel_bump_stan: 활동 채널 전체를 한 번에 잠그는 다중행 UPDATE
   (호출자 0인 잠복 지뢰). 시퀀스 채번 + 집계 조회로 교체.

화면
- 기본값을 실존 시드 키로 부팅 시 조회해 덮어쓴다. 기존 플레이스홀더
  (card_limit 에 없는 카드, 8자리 영업일)로는 처리성 화면이 전부 거절당했다.
- 게이트웨이가 단일 스레드 직렬 루프라 기본값 조회도 순차로 수행.

검증 (실측)
- 통합 빌드: modules=11 servers=11 batches=506, error 0
- 도메인: 45 프로세스, 1,466 서비스 AVAIL
- 체인 전수 스모크 477/477 응답, TPETIME/TPEABORT 0건, 최대 530ms
- AUTH: 수정 전 60초 블록→TPEABORT, 수정 후 0.215초 ok:true
- 6단 매입 체인 COMMIT OK, 미결 2PC 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hyeongwoo 2026-07-21 14:55:49 +00:00
parent 4cede852a0
commit 074d244bba
654 changed files with 36008 additions and 2805 deletions

View file

@ -28,6 +28,13 @@ int pydb_payout_reject_reason(long payout_id, char *out)
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT coalesce(reject_reason,'') INTO :h_reason FROM py_payout WHERE payout_id = :h_pay;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) { userlog("pydb_payout_reject_reason FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; }
strncpy(out, h_reason, 127); out[127] = 0;
/* 실제 값 길이만 복사 (strncpy 의 NUL 패딩으로 호출자 스택을 넘어 쓰는 것 방지).
* 상세 사유는 cm_param_dbio.pgc 의 cmdb_get_param 주석 참조. */
{
size_t vn = strlen(h_reason);
if (vn > 127) vn = 127;
memcpy(out, h_reason, vn);
out[vn] = 0;
}
return 0;
}

View file

@ -132,5 +132,66 @@ void PAY_ACCTCHK(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_ACCTCHK -> LG_POST (계좌검증-원장반영) =====
* XA 규율: tpcall 로 부르는 LG_POST 는 별도 XA 브랜치라 PAY_ACCTCHK 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_ACCTCHK", 0L);
Bchg(b, T_REASON, 0, "계좌검증-원장반영", 0L);
userlog("[PAY_ACCTCHK] 계좌검증-원장반영: 체인 호출 -> LG_POST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_POST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_ACCTCHK] tpcall(LG_POST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8811);
Bchg(b, T_MSG, 0, "계좌검증 원장반영(LG_POST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_ACCTCHK 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_ACCTCHK", 0L);
setl(b, T_RC, 0);
userlog("[PAY_ACCTCHK] 계좌검증-원장반영: 체인 완료 <- LG_POST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -145,5 +145,75 @@ void PAY_ACCTVERIFY(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_ACCTVERIFY -> PAY_ACCTCHK (계좌실명-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_ACCTVERIFY 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_ACCTVERIFY", 0L);
Bchg(b, T_REASON, 0, "계좌실명-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_ACCTVERIFY] 계좌실명-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_ACCTVERIFY] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8828);
Bchg(b, T_MSG, 0, "계좌실명 검증 체인 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_ACCTVERIFY 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_ACCTVERIFY", 0L);
setl(b, T_RC, 0);
userlog("[PAY_ACCTVERIFY] 계좌실명-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -181,5 +181,71 @@ void PAY_ADJUST(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_ADJUST -> ST_ADJUST (지급조정-정산조정) =====
* XA 규율: tpcall 로 부르는 ST_ADJUST 는 별도 XA 브랜치라 PAY_ADJUST 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_ADJUST", 0L);
Bchg(b, T_REASON, 0, "지급조정-정산조정", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_ADJUST] 지급조정-정산조정: 체인 호출 -> ST_ADJUST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("ST_ADJUST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_ADJUST] tpcall(ST_ADJUST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8820);
Bchg(b, T_MSG, 0, "지급조정 정산조정(ST_ADJUST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_ADJUST 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_ADJUST", 0L);
setl(b, T_RC, 0);
userlog("[PAY_ADJUST] 지급조정-정산조정: 체인 완료 <- ST_ADJUST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -20,6 +20,7 @@ void PAY_APPRBULK(TPSVCINFO *p)
long h_wlim;
long h_wused;
long h_waff;
long h_wfid;
long h_wval;
long h_wrate;
char h_wst[8];
@ -123,5 +124,102 @@ void PAY_APPRBULK(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_APPRBULK -> PAY_FILEGEN (일괄승인-지급파일생성) =====
* XA 규율: tpcall 로 부르는 PAY_FILEGEN 는 별도 XA 브랜치라 PAY_APPRBULK 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_APPRBULK", 0L);
Bchg(b, T_REASON, 0, "일괄승인-지급파일생성", 0L);
/* 체인 표식 + 위임 편성분 전달: 방금 이 브랜치에서 'A' 로 일괄 전이한 지급건은
* 하위(PAY_FILEGEN)의 스냅샷에 보이지 않고, 하위가 그 행들을 다시 UPDATE 하면
* 형제 XA 브랜치끼리 미커밋 행락으로 교착된다. 따라서 승인 건수/금액만 넘기고
* py_payout 편성 UPDATE 는 행 소유자인 이 브랜치가 복귀 후 수행한다. */
Bchg(b, T_STR8, 0, "CHAIN", 0L);
setl(b, T_ARG7, h_waff);
setl(b, T_ARG8, h_wsum > 0 ? h_wsum : 0);
Bdel(b, T_STR7, 0);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_APPRBULK] 일괄승인-지급파일생성: 체인 호출 -> PAY_FILEGEN pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_FILEGEN", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_APPRBULK] tpcall(PAY_FILEGEN) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8843);
Bchg(b, T_MSG, 0, "일괄승인 파일생성 체인 실패", 0L);
FAIL(b);
}
/* --- 위임 편성 반영부: 하위가 채번한 지급파일로의 편성을, 행 소유자인
* 이 브랜치에서 자신이 일괄 승인한 건에 대해 단 한 번만 반영한다. --- */
{
char dg_mk[16];
dg_mk[0] = 0;
gets_(b, T_STR7, dg_mk, sizeof(dg_mk));
if (strcmp(dg_mk, "FILEASSIGN") == 0) {
h_wfid = getl(b, T_ID2);
if (h_wfid > 0) {
EXEC SQL UPDATE py_payout SET file_id = :h_wfid, status = 'F',
updated_at = now()
WHERE biz_date = :h_bizdate AND status = 'A' AND net <= :h_amt1;
if (sqlca.sqlcode < 0) {
setl(b, T_RC, 53);
Bchg(b, T_MSG, 0, "일괄 지급건 파일 편성(위임) 실패", 0L);
FAIL(b);
}
userlog("[PAY_APPRBULK] 위임 편성 반영 완료 file=%ld aff=%ld",
h_wfid, (long)sqlca.sqlerrd[2]);
}
Bdel(b, T_STR7, 0);
}
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_APPRBULK 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_APPRBULK", 0L);
setl(b, T_RC, 0);
userlog("[PAY_APPRBULK] 일괄승인-지급파일생성: 체인 완료 <- PAY_FILEGEN pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -19,6 +19,7 @@ void PAY_APPROVE(TPSVCINFO *p)
char h_wchan[16];
long h_wretry;
long h_waff;
long h_wfid;
char h_wmerch[64];
char h_wacct[64];
long h_wcnt;
@ -151,5 +152,102 @@ void PAY_APPROVE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_APPROVE -> PAY_FILEGEN (지급승인-지급파일생성) =====
* XA 규율: tpcall 로 부르는 PAY_FILEGEN 는 별도 XA 브랜치라 PAY_APPROVE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_APPROVE", 0L);
Bchg(b, T_REASON, 0, "지급승인-지급파일생성", 0L);
/* 체인 표식 + 위임 편성분 전달: 방금 이 브랜치에서 'A' 로 전이한 지급건은
* 하위(PAY_FILEGEN)의 스냅샷에 보이지 않고, 하위가 그 행을 다시 UPDATE 하면
* 형제 XA 브랜치끼리 미커밋 행락으로 교착된다. 따라서 승인 건수/금액만 넘기고
* py_payout 편성 UPDATE 는 행 소유자인 이 브랜치가 복귀 후 수행한다. */
Bchg(b, T_STR8, 0, "CHAIN", 0L);
setl(b, T_ARG7, h_waff);
setl(b, T_ARG8, h_wnet > 0 ? h_wnet : 0);
Bdel(b, T_STR7, 0);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_APPROVE] 지급승인-지급파일생성: 체인 호출 -> PAY_FILEGEN pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_FILEGEN", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_APPROVE] tpcall(PAY_FILEGEN) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8842);
Bchg(b, T_MSG, 0, "지급승인 파일생성 체인 실패", 0L);
FAIL(b);
}
/* --- 위임 편성 반영부: 하위가 채번한 지급파일로의 편성을, 행 소유자인
* 이 브랜치에서 자신이 승인한 건에 대해 단 한 번만 반영한다. --- */
{
char dg_mk[16];
dg_mk[0] = 0;
gets_(b, T_STR7, dg_mk, sizeof(dg_mk));
if (strcmp(dg_mk, "FILEASSIGN") == 0) {
h_wfid = getl(b, T_ID2);
if (h_wfid > 0) {
EXEC SQL UPDATE py_payout SET file_id = :h_wfid, status = 'F',
updated_at = now()
WHERE payout_id = :h_id1 AND status = 'A';
if (sqlca.sqlcode < 0) {
setl(b, T_RC, 53);
Bchg(b, T_MSG, 0, "지급건 파일 편성(위임) 실패", 0L);
FAIL(b);
}
userlog("[PAY_APPROVE] 위임 편성 반영 완료 payout=%ld file=%ld aff=%ld",
h_id1, h_wfid, (long)sqlca.sqlerrd[2]);
}
Bdel(b, T_STR7, 0);
}
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_APPROVE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_APPROVE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_APPROVE] 지급승인-지급파일생성: 체인 완료 <- PAY_FILEGEN pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -166,5 +166,73 @@ void PAY_BANKFILE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_BANKFILE -> MG_SEND (은행파일-이체전문송신) =====
* XA 규율: tpcall 로 부르는 MG_SEND 는 별도 XA 브랜치라 PAY_BANKFILE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_BANKFILE", 0L);
Bchg(b, T_REASON, 0, "은행파일-이체전문송신", 0L);
{
char cx_ch[16];
gets_(b, T_CHANNEL, cx_ch, sizeof(cx_ch));
if (strlen(cx_ch) < 2) Bchg(b, T_CHANNEL, 0, "CH01", 0L);
}
Bchg(b, T_STR1, 0, "0200", 0L);
userlog("[PAY_BANKFILE] 은행파일-이체전문송신: 체인 호출 -> MG_SEND pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_BANKFILE] tpcall(MG_SEND) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8837);
Bchg(b, T_MSG, 0, "은행파일 전문송신(MG_SEND) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_BANKFILE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_BANKFILE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_BANKFILE] 은행파일-이체전문송신: 체인 완료 <- MG_SEND pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -124,5 +124,75 @@ void PAY_BANKVERIFY(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_BANKVERIFY -> PAY_ACCTCHK (은행검증-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_BANKVERIFY 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_BANKVERIFY", 0L);
Bchg(b, T_REASON, 0, "은행검증-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_BANKVERIFY] 은행검증-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_BANKVERIFY] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8829);
Bchg(b, T_MSG, 0, "은행검증 체인 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_BANKVERIFY 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_BANKVERIFY", 0L);
setl(b, T_RC, 0);
userlog("[PAY_BANKVERIFY] 은행검증-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -173,5 +173,131 @@ void PAY_CANCEL(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_CANCEL -> LG_REVERSE (지급취소-원장역분개) =====
* XA 규율: tpcall 로 부르는 LG_REVERSE 는 별도 XA 브랜치라 PAY_CANCEL 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_CANCEL", 0L);
Bchg(b, T_REASON, 0, "지급취소-원장역분개", 0L);
userlog("[PAY_CANCEL] 지급취소-원장역분개: 체인 호출 -> LG_REVERSE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_REVERSE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_CANCEL] tpcall(LG_REVERSE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8814);
Bchg(b, T_MSG, 0, "지급취소 역분개(LG_REVERSE) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_CANCEL 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_CANCEL", 0L);
setl(b, T_RC, 0);
userlog("[PAY_CANCEL] 지급취소-원장역분개: 체인 완료 <- LG_REVERSE pid=%ld", cx_pid);
}
/* ===== 8c) 업무 체인 호출부: PAY_CANCEL -> ST_ADJUST (지급취소-정산조정) =====
* XA 규율: tpcall 로 부르는 ST_ADJUST 는 별도 XA 브랜치라 PAY_CANCEL 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_CANCEL", 0L);
Bchg(b, T_REASON, 0, "지급취소-정산조정", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_CANCEL] 지급취소-정산조정: 체인 호출 -> ST_ADJUST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("ST_ADJUST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_CANCEL] tpcall(ST_ADJUST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8815);
Bchg(b, T_MSG, 0, "지급취소 정산조정(ST_ADJUST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_CANCEL 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_CANCEL", 0L);
setl(b, T_RC, 0);
userlog("[PAY_CANCEL] 지급취소-정산조정: 체인 완료 <- ST_ADJUST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -178,5 +178,66 @@ void PAY_CHARGEBACK(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_CHARGEBACK -> LG_REVERSE (지급차지백-원장역분개) =====
* XA 규율: tpcall 로 부르는 LG_REVERSE 는 별도 XA 브랜치라 PAY_CHARGEBACK 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_CHARGEBACK", 0L);
Bchg(b, T_REASON, 0, "지급차지백-원장역분개", 0L);
userlog("[PAY_CHARGEBACK] 지급차지백-원장역분개: 체인 호출 -> LG_REVERSE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_REVERSE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_CHARGEBACK] tpcall(LG_REVERSE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8845);
Bchg(b, T_MSG, 0, "지급차지백 역분개 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_CHARGEBACK 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_CHARGEBACK", 0L);
setl(b, T_RC, 0);
userlog("[PAY_CHARGEBACK] 지급차지백-원장역분개: 체인 완료 <- LG_REVERSE pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -135,5 +135,66 @@ void PAY_CLOSE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_CLOSE -> LG_POST (지급마감-원장반영) =====
* XA 규율: tpcall 로 부르는 LG_POST 는 별도 XA 브랜치라 PAY_CLOSE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_CLOSE", 0L);
Bchg(b, T_REASON, 0, "지급마감-원장반영", 0L);
userlog("[PAY_CLOSE] 지급마감-원장반영: 체인 호출 -> LG_POST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_POST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_CLOSE] tpcall(LG_POST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8848);
Bchg(b, T_MSG, 0, "지급마감 원장반영(LG_POST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_CLOSE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_CLOSE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_CLOSE] 지급마감-원장반영: 체인 완료 <- LG_POST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -180,5 +180,75 @@ void PAY_COMBINE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_COMBINE -> PAY_ACCTCHK (지급합산-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_COMBINE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_COMBINE", 0L);
Bchg(b, T_REASON, 0, "지급합산-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_COMBINE] 지급합산-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_COMBINE] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8826);
Bchg(b, T_MSG, 0, "지급합산 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_COMBINE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_COMBINE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_COMBINE] 지급합산-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -144,5 +144,66 @@ void PAY_CONFIRM(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_CONFIRM -> LG_POST (지급확정-원장반영) =====
* XA 규율: tpcall 로 부르는 LG_POST 는 별도 XA 브랜치라 PAY_CONFIRM 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_CONFIRM", 0L);
Bchg(b, T_REASON, 0, "지급확정-원장반영", 0L);
userlog("[PAY_CONFIRM] 지급확정-원장반영: 체인 호출 -> LG_POST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_POST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_CONFIRM] tpcall(LG_POST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8813);
Bchg(b, T_MSG, 0, "지급확정 원장반영(LG_POST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_CONFIRM 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_CONFIRM", 0L);
setl(b, T_RC, 0);
userlog("[PAY_CONFIRM] 지급확정-원장반영: 체인 완료 <- LG_POST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -33,6 +33,10 @@ void PAY_FILEGEN(TPSVCINFO *p)
long h_wz2;
long h_wz3;
EXEC SQL END DECLARE SECTION;
/* 체인 하위 호출 표식 / 상위가 자기 브랜치에서 이미 승인(A)한 위임 편성분 */
char ch_mark[16];
int in_chain = 0;
long ch_pcnt = 0, ch_pamt = 0;
/* 1) 입력 검증부 - 모든 입력 필드의 필수/범위/형식 검사 */
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
@ -51,6 +55,26 @@ void PAY_FILEGEN(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "편성 대상 집계 오류", 0L);
FAIL(b);
}
/* 체인 하위 호출 판별 (T_STR8 = "CHAIN"): 상위(PAY_APPROVE/PAY_APPRBULK)가 이미
* 자기 XA 브랜치에서 py_payout 행을 'A' 로 전이해 잠근 채 이 서비스를 부른 상태다.
* 같은 글로벌 트랜잭션의 다른 XA 브랜치가 그 행을 다시 UPDATE 하면 미커밋 행락을
* 서로 기다려 트랜잭션 타임아웃까지 교착된다. 게다가 상위의 승인분은 이 브랜치의
* 스냅샷에 보이지 않아 위 집계에서 누락되므로 파일 헤더 수치까지 틀어진다.
* 따라서 체인 하위에서는 (1) 상위가 전문(T_ARG7/T_ARG8)으로 넘긴 승인분을 편성
* 대상에 합산하고, (2) py_payout 행 편성 UPDATE 는 소유자(상위)에게 위임한다.
* 파일 채번/헤더 생성/정책 판정/이력 적재는 그대로 이 서비스가 수행한다. */
gets_(b, T_STR8, ch_mark, sizeof(ch_mark));
if (strcmp(ch_mark, "CHAIN") == 0) {
in_chain = 1;
ch_pcnt = getl(b, T_ARG7);
ch_pamt = getl(b, T_ARG8);
if (ch_pcnt < 0) ch_pcnt = 0;
if (ch_pamt < 0) ch_pamt = 0;
h_wrcnt += ch_pcnt;
h_wramt += ch_pamt;
userlog("PAY_FILEGEN 체인 하위 호출 - 상위 승인분 %ld건 %ld원 합산, py_payout 편성은 상위에 위임",
ch_pcnt, ch_pamt);
}
h_whcnt = h_wrcnt; h_whamt = h_wramt;
strcpy(h_wfst, "G");
strncpy(h_wfdate, h_bizdate, sizeof(h_wfdate)-1);
@ -99,15 +123,27 @@ void PAY_FILEGEN(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "지급파일 헤더 생성 실패", 0L);
FAIL(b);
}
EXEC SQL UPDATE py_payout SET file_id = :h_wnew, status = 'F',
updated_at = now()
WHERE biz_date = :h_bizdate AND status = 'A';
if (sqlca.sqlcode < 0) {
setl(b, T_RC, 52);
Bchg(b, T_MSG, 0, "지급건 파일 편성 실패", 0L);
FAIL(b);
if (!in_chain) {
/* 행 소유자 브랜치: 당일 승인(A) 지급건을 직접 이 파일로 편성한다 */
EXEC SQL UPDATE py_payout SET file_id = :h_wnew, status = 'F',
updated_at = now()
WHERE biz_date = :h_bizdate AND status = 'A';
if (sqlca.sqlcode < 0) {
setl(b, T_RC, 52);
Bchg(b, T_MSG, 0, "지급건 파일 편성 실패", 0L);
FAIL(b);
}
h_waff = sqlca.sqlerrd[2];
} else {
/* 체인 하위 브랜치: py_payout 편성 UPDATE 를 하지 않고, 채번된 파일번호를
* 전문에 실어 소유자(상위)에게 위임한다. 상위가 tpcall 복귀 후 자기 브랜치에서
* 자신이 승인한 행에 대해 한 번만 file_id/status='F' 를 반영한다. */
Bchg(b, T_STR7, 0, "FILEASSIGN", 0L);
setl(b, T_ID2, h_wnew);
h_waff = ch_pcnt;
userlog("PAY_FILEGEN 체인 하위 - 파일편성(file=%ld %ld건)을 상위 소유자에 위임",
h_wnew, ch_pcnt);
}
h_waff = sqlca.sqlerrd[2];
strcpy(h_lvl, "I");
snprintf(h_amsg, sizeof(h_amsg), "%s file=%ld 상세=%ld건 %ld원 반영=%ld", "PAY_FILEGEN", h_wnew, h_wrcnt, h_wramt, h_waff);
EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
@ -154,5 +190,75 @@ void PAY_FILEGEN(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_FILEGEN -> PAY_ACCTCHK (지급파일생성-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_FILEGEN 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_FILEGEN", 0L);
Bchg(b, T_REASON, 0, "지급파일생성-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_FILEGEN] 지급파일생성-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_FILEGEN] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8810);
Bchg(b, T_MSG, 0, "계좌검증(PAY_ACCTCHK) 체인 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_FILEGEN 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_FILEGEN", 0L);
setl(b, T_RC, 0);
userlog("[PAY_FILEGEN] 지급파일생성-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -136,5 +136,75 @@ void PAY_FILEREBUILD(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_FILEREBUILD -> PAY_ACCTCHK (파일재생성-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_FILEREBUILD 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_FILEREBUILD", 0L);
Bchg(b, T_REASON, 0, "파일재생성-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_FILEREBUILD] 파일재생성-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_FILEREBUILD] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8831);
Bchg(b, T_MSG, 0, "파일 재생성 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_FILEREBUILD 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_FILEREBUILD", 0L);
setl(b, T_RC, 0);
userlog("[PAY_FILEREBUILD] 파일재생성-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -150,5 +150,71 @@ void PAY_HOLD(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_HOLD -> ST_HOLD (지급보류-정산보류연동) =====
* XA 규율: tpcall 로 부르는 ST_HOLD 는 별도 XA 브랜치라 PAY_HOLD 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_HOLD", 0L);
Bchg(b, T_REASON, 0, "지급보류-정산보류연동", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_HOLD] 지급보류-정산보류연동: 체인 호출 -> ST_HOLD pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("ST_HOLD", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_HOLD] tpcall(ST_HOLD) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8821);
Bchg(b, T_MSG, 0, "지급보류 정산연동(ST_HOLD) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_HOLD 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_HOLD", 0L);
setl(b, T_RC, 0);
userlog("[PAY_HOLD] 지급보류-정산보류연동: 체인 완료 <- ST_HOLD pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -181,5 +181,75 @@ void PAY_MERGE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_MERGE -> PAY_ACCTCHK (지급병합-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_MERGE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_MERGE", 0L);
Bchg(b, T_REASON, 0, "지급병합-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_MERGE] 지급병합-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_MERGE] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8827);
Bchg(b, T_MSG, 0, "지급병합 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_MERGE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_MERGE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_MERGE] 지급병합-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -172,5 +172,66 @@ void PAY_NETOFF(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_NETOFF -> LG_POST (지급상계-원장반영) =====
* XA 규율: tpcall 로 부르는 LG_POST 는 별도 XA 브랜치라 PAY_NETOFF 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_NETOFF", 0L);
Bchg(b, T_REASON, 0, "지급상계-원장반영", 0L);
userlog("[PAY_NETOFF] 지급상계-원장반영: 체인 호출 -> LG_POST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_POST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_NETOFF] tpcall(LG_POST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8847);
Bchg(b, T_MSG, 0, "지급상계 원장반영(LG_POST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_NETOFF 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_NETOFF", 0L);
setl(b, T_RC, 0);
userlog("[PAY_NETOFF] 지급상계-원장반영: 체인 완료 <- LG_POST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -174,5 +174,131 @@ void PAY_RECLAIM(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_RECLAIM -> LG_REVERSE (지급환수-원장역분개) =====
* XA 규율: tpcall 로 부르는 LG_REVERSE 는 별도 XA 브랜치라 PAY_RECLAIM 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RECLAIM", 0L);
Bchg(b, T_REASON, 0, "지급환수-원장역분개", 0L);
userlog("[PAY_RECLAIM] 지급환수-원장역분개: 체인 호출 -> LG_REVERSE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_REVERSE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RECLAIM] tpcall(LG_REVERSE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8818);
Bchg(b, T_MSG, 0, "지급환수 역분개(LG_REVERSE) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RECLAIM 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RECLAIM", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RECLAIM] 지급환수-원장역분개: 체인 완료 <- LG_REVERSE pid=%ld", cx_pid);
}
/* ===== 8c) 업무 체인 호출부: PAY_RECLAIM -> ST_ADJUST (지급환수-정산조정) =====
* XA 규율: tpcall 로 부르는 ST_ADJUST 는 별도 XA 브랜치라 PAY_RECLAIM 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RECLAIM", 0L);
Bchg(b, T_REASON, 0, "지급환수-정산조정", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_RECLAIM] 지급환수-정산조정: 체인 호출 -> ST_ADJUST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("ST_ADJUST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RECLAIM] tpcall(ST_ADJUST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8819);
Bchg(b, T_MSG, 0, "지급환수 정산조정(ST_ADJUST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RECLAIM 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RECLAIM", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RECLAIM] 지급환수-정산조정: 체인 완료 <- ST_ADJUST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -169,5 +169,66 @@ void PAY_REFUND(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_REFUND -> LG_REVERSE (지급환불-원장역분개) =====
* XA 규율: tpcall 로 부르는 LG_REVERSE 는 별도 XA 브랜치라 PAY_REFUND 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_REFUND", 0L);
Bchg(b, T_REASON, 0, "지급환불-원장역분개", 0L);
userlog("[PAY_REFUND] 지급환불-원장역분개: 체인 호출 -> LG_REVERSE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_REVERSE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_REFUND] tpcall(LG_REVERSE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8844);
Bchg(b, T_MSG, 0, "지급환불 역분개(LG_REVERSE) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_REFUND 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_REFUND", 0L);
setl(b, T_RC, 0);
userlog("[PAY_REFUND] 지급환불-원장역분개: 체인 완료 <- LG_REVERSE pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -151,5 +151,71 @@ void PAY_RELEASE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_RELEASE -> ST_RELEASE (지급해제-정산해제연동) =====
* XA 규율: tpcall 로 부르는 ST_RELEASE 는 별도 XA 브랜치라 PAY_RELEASE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RELEASE", 0L);
Bchg(b, T_REASON, 0, "지급해제-정산해제연동", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_RELEASE] 지급해제-정산해제연동: 체인 호출 -> ST_RELEASE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("ST_RELEASE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RELEASE] tpcall(ST_RELEASE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8822);
Bchg(b, T_MSG, 0, "지급해제 정산연동(ST_RELEASE) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RELEASE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RELEASE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RELEASE] 지급해제-정산해제연동: 체인 완료 <- ST_RELEASE pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -140,5 +140,73 @@ void PAY_REMIT(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_REMIT -> MG_SEND (송금-이체전문송신) =====
* XA 규율: tpcall 로 부르는 MG_SEND 는 별도 XA 브랜치라 PAY_REMIT 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_REMIT", 0L);
Bchg(b, T_REASON, 0, "송금-이체전문송신", 0L);
{
char cx_ch[16];
gets_(b, T_CHANNEL, cx_ch, sizeof(cx_ch));
if (strlen(cx_ch) < 2) Bchg(b, T_CHANNEL, 0, "CH01", 0L);
}
Bchg(b, T_STR1, 0, "0200", 0L);
userlog("[PAY_REMIT] 송금-이체전문송신: 체인 호출 -> MG_SEND pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_REMIT] tpcall(MG_SEND) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8836);
Bchg(b, T_MSG, 0, "송금 전문송신(MG_SEND) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_REMIT 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_REMIT", 0L);
setl(b, T_RC, 0);
userlog("[PAY_REMIT] 송금-이체전문송신: 체인 완료 <- MG_SEND pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -153,5 +153,71 @@ void PAY_REPROC(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_REPROC -> PAY_FAILRSN (지급재처리-실패사유확인) =====
* XA 규율: tpcall 로 부르는 PAY_FAILRSN 는 별도 XA 브랜치라 PAY_REPROC 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_REPROC", 0L);
Bchg(b, T_REASON, 0, "지급재처리-실패사유확인", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_REPROC] 지급재처리-실패사유확인: 체인 호출 -> PAY_FAILRSN pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_FAILRSN", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_REPROC] tpcall(PAY_FAILRSN) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8841);
Bchg(b, T_MSG, 0, "재처리 실패사유 확인 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_REPROC 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_REPROC", 0L);
setl(b, T_RC, 0);
userlog("[PAY_REPROC] 지급재처리-실패사유확인: 체인 완료 <- PAY_FAILRSN pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -137,5 +137,73 @@ void PAY_REQUEUE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_REQUEUE -> MG_SEND (재큐잉-이체전문재송신) =====
* XA 규율: tpcall 로 부르는 MG_SEND 는 별도 XA 브랜치라 PAY_REQUEUE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_REQUEUE", 0L);
Bchg(b, T_REASON, 0, "재큐잉-이체전문재송신", 0L);
{
char cx_ch[16];
gets_(b, T_CHANNEL, cx_ch, sizeof(cx_ch));
if (strlen(cx_ch) < 2) Bchg(b, T_CHANNEL, 0, "CH01", 0L);
}
Bchg(b, T_STR1, 0, "0200", 0L);
userlog("[PAY_REQUEUE] 재큐잉-이체전문재송신: 체인 호출 -> MG_SEND pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_REQUEUE] tpcall(MG_SEND) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8838);
Bchg(b, T_MSG, 0, "재큐잉 전문 재송신(MG_SEND) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_REQUEUE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_REQUEUE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_REQUEUE] 재큐잉-이체전문재송신: 체인 완료 <- MG_SEND pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -150,5 +150,66 @@ void PAY_RESULT(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_RESULT -> LG_POST (지급결과-원장반영) =====
* XA 규율: tpcall 로 부르는 LG_POST 는 별도 XA 브랜치라 PAY_RESULT 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RESULT", 0L);
Bchg(b, T_REASON, 0, "지급결과-원장반영", 0L);
userlog("[PAY_RESULT] 지급결과-원장반영: 체인 호출 -> LG_POST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_POST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RESULT] tpcall(LG_POST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8812);
Bchg(b, T_MSG, 0, "지급결과 원장반영(LG_POST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RESULT 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RESULT", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RESULT] 지급결과-원장반영: 체인 완료 <- LG_POST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -138,5 +138,69 @@ void PAY_RESVMODIFY(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_RESVMODIFY -> CM_BIZDAY (예약변경-영업일계산) =====
* XA 규율: tpcall 로 부르는 CM_BIZDAY 는 별도 XA 브랜치라 PAY_RESVMODIFY 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RESVMODIFY", 0L);
Bchg(b, T_REASON, 0, "예약변경-영업일계산", 0L);
Bchg(b, T_STR1, 0, "BIZDAY", 0L);
if (getl(b, T_ARG1) <= 0) setl(b, T_ARG1, 1);
userlog("[PAY_RESVMODIFY] 예약변경-영업일계산: 체인 호출 -> CM_BIZDAY pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("CM_BIZDAY", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RESVMODIFY] tpcall(CM_BIZDAY) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8833);
Bchg(b, T_MSG, 0, "예약변경 영업일계산 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RESVMODIFY 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RESVMODIFY", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RESVMODIFY] 예약변경-영업일계산: 체인 완료 <- CM_BIZDAY pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -152,5 +152,71 @@ void PAY_RETRY(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_RETRY -> PAY_FAILRSN (지급재시도-실패사유확인) =====
* XA 규율: tpcall 로 부르는 PAY_FAILRSN 는 별도 XA 브랜치라 PAY_RETRY 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RETRY", 0L);
Bchg(b, T_REASON, 0, "지급재시도-실패사유확인", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_RETRY] 지급재시도-실패사유확인: 체인 호출 -> PAY_FAILRSN pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_FAILRSN", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RETRY] tpcall(PAY_FAILRSN) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8839);
Bchg(b, T_MSG, 0, "실패사유 확인(PAY_FAILRSN) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RETRY 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RETRY", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RETRY] 지급재시도-실패사유확인: 체인 완료 <- PAY_FAILRSN pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -154,5 +154,71 @@ void PAY_RETRYONE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_RETRYONE -> PAY_FAILRSN (단건재시도-실패사유확인) =====
* XA 규율: tpcall 로 부르는 PAY_FAILRSN 는 별도 XA 브랜치라 PAY_RETRYONE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_RETRYONE", 0L);
Bchg(b, T_REASON, 0, "단건재시도-실패사유확인", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_RETRYONE] 단건재시도-실패사유확인: 체인 호출 -> PAY_FAILRSN pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_FAILRSN", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_RETRYONE] tpcall(PAY_FAILRSN) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8840);
Bchg(b, T_MSG, 0, "단건 실패사유 확인 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_RETRYONE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_RETRYONE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_RETRYONE] 단건재시도-실패사유확인: 체인 완료 <- PAY_FAILRSN pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -171,5 +171,131 @@ void PAY_REVERSE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_REVERSE -> LG_REVERSE (지급반환-원장역분개) =====
* XA 규율: tpcall 로 부르는 LG_REVERSE 는 별도 XA 브랜치라 PAY_REVERSE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_REVERSE", 0L);
Bchg(b, T_REASON, 0, "지급반환-원장역분개", 0L);
userlog("[PAY_REVERSE] 지급반환-원장역분개: 체인 호출 -> LG_REVERSE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_REVERSE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_REVERSE] tpcall(LG_REVERSE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8816);
Bchg(b, T_MSG, 0, "지급반환 역분개(LG_REVERSE) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_REVERSE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_REVERSE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_REVERSE] 지급반환-원장역분개: 체인 완료 <- LG_REVERSE pid=%ld", cx_pid);
}
/* ===== 8c) 업무 체인 호출부: PAY_REVERSE -> ST_ADJUST (지급반환-정산조정) =====
* XA 규율: tpcall 로 부르는 ST_ADJUST 는 별도 XA 브랜치라 PAY_REVERSE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_REVERSE", 0L);
Bchg(b, T_REASON, 0, "지급반환-정산조정", 0L);
{
long cx_dl = getl(b, T_DELTA);
if (cx_dl > 100000000L || cx_dl < -100000000L) setl(b, T_DELTA, 0);
}
userlog("[PAY_REVERSE] 지급반환-정산조정: 체인 호출 -> ST_ADJUST pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("ST_ADJUST", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_REVERSE] tpcall(ST_ADJUST) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8817);
Bchg(b, T_MSG, 0, "지급반환 정산조정(ST_ADJUST) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_REVERSE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_REVERSE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_REVERSE] 지급반환-정산조정: 체인 완료 <- ST_ADJUST pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -155,5 +155,69 @@ void PAY_ROLLFWD(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_ROLLFWD -> CM_BIZDAY (지급이월-영업일계산) =====
* XA 규율: tpcall 로 부르는 CM_BIZDAY 는 별도 XA 브랜치라 PAY_ROLLFWD 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_ROLLFWD", 0L);
Bchg(b, T_REASON, 0, "지급이월-영업일계산", 0L);
Bchg(b, T_STR1, 0, "BIZDAY", 0L);
if (getl(b, T_ARG1) <= 0) setl(b, T_ARG1, 1);
userlog("[PAY_ROLLFWD] 지급이월-영업일계산: 체인 호출 -> CM_BIZDAY pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("CM_BIZDAY", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_ROLLFWD] tpcall(CM_BIZDAY) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8834);
Bchg(b, T_MSG, 0, "지급이월 영업일계산 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_ROLLFWD 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_ROLLFWD", 0L);
setl(b, T_RC, 0);
userlog("[PAY_ROLLFWD] 지급이월-영업일계산: 체인 완료 <- CM_BIZDAY pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -141,5 +141,69 @@ void PAY_SCHEDULE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_SCHEDULE -> CM_BIZDAY (지급예약-영업일계산) =====
* XA 규율: tpcall 로 부르는 CM_BIZDAY 는 별도 XA 브랜치라 PAY_SCHEDULE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_SCHEDULE", 0L);
Bchg(b, T_REASON, 0, "지급예약-영업일계산", 0L);
Bchg(b, T_STR1, 0, "BIZDAY", 0L);
if (getl(b, T_ARG1) <= 0) setl(b, T_ARG1, 1);
userlog("[PAY_SCHEDULE] 지급예약-영업일계산: 체인 호출 -> CM_BIZDAY pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("CM_BIZDAY", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_SCHEDULE] tpcall(CM_BIZDAY) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8832);
Bchg(b, T_MSG, 0, "지급예약 영업일계산(CM_BIZDAY) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_SCHEDULE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_SCHEDULE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_SCHEDULE] 지급예약-영업일계산: 체인 완료 <- CM_BIZDAY pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -226,5 +226,75 @@ void PAY_SPLIT(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_SPLIT -> PAY_ACCTCHK (분할지급-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_SPLIT 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_SPLIT", 0L);
Bchg(b, T_REASON, 0, "분할지급-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_SPLIT] 분할지급-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_SPLIT] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8823);
Bchg(b, T_MSG, 0, "분할지급 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_SPLIT 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_SPLIT", 0L);
setl(b, T_RC, 0);
userlog("[PAY_SPLIT] 분할지급-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -186,5 +186,75 @@ void PAY_SPLITEVEN(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_SPLITEVEN -> PAY_ACCTCHK (균등분할-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_SPLITEVEN 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_SPLITEVEN", 0L);
Bchg(b, T_REASON, 0, "균등분할-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_SPLITEVEN] 균등분할-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_SPLITEVEN] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8824);
Bchg(b, T_MSG, 0, "균등분할 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_SPLITEVEN 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_SPLITEVEN", 0L);
setl(b, T_RC, 0);
userlog("[PAY_SPLITEVEN] 균등분할-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -177,5 +177,75 @@ void PAY_SPLITRATIO(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_SPLITRATIO -> PAY_ACCTCHK (비율분할-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_SPLITRATIO 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_SPLITRATIO", 0L);
Bchg(b, T_REASON, 0, "비율분할-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_SPLITRATIO] 비율분할-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_SPLITRATIO] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8825);
Bchg(b, T_MSG, 0, "비율분할 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_SPLITRATIO 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_SPLITRATIO", 0L);
setl(b, T_RC, 0);
userlog("[PAY_SPLITRATIO] 비율분할-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -227,5 +227,75 @@ void PAY_URGENT(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_URGENT -> PAY_ACCTCHK (긴급지급-계좌검증) =====
* XA 규율: tpcall 로 부르는 PAY_ACCTCHK 는 별도 XA 브랜치라 PAY_URGENT 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_URGENT", 0L);
Bchg(b, T_REASON, 0, "긴급지급-계좌검증", 0L);
{
char cx_k1[64];
gets_(b, T_KEY1, cx_k1, sizeof(cx_k1));
if (strlen(cx_k1) < 3) {
if (strlen(cx_mid) >= 3) Bchg(b, T_KEY1, 0, cx_mid, 0L);
else Bchg(b, T_KEY1, 0, "ACCT00000001", 0L);
}
}
userlog("[PAY_URGENT] 긴급지급-계좌검증: 체인 호출 -> PAY_ACCTCHK pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("PAY_ACCTCHK", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_URGENT] tpcall(PAY_ACCTCHK) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8830);
Bchg(b, T_MSG, 0, "긴급지급 계좌검증 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_URGENT 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_URGENT", 0L);
setl(b, T_RC, 0);
userlog("[PAY_URGENT] 긴급지급-계좌검증: 체인 완료 <- PAY_ACCTCHK pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -161,5 +161,66 @@ void PAY_VOIDONE(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_VOIDONE -> LG_REVERSE (지급무효-원장역분개) =====
* XA 규율: tpcall 로 부르는 LG_REVERSE 는 별도 XA 브랜치라 PAY_VOIDONE 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_VOIDONE", 0L);
Bchg(b, T_REASON, 0, "지급무효-원장역분개", 0L);
userlog("[PAY_VOIDONE] 지급무효-원장역분개: 체인 호출 -> LG_REVERSE pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("LG_REVERSE", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_VOIDONE] tpcall(LG_REVERSE) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8846);
Bchg(b, T_MSG, 0, "지급무효 역분개(LG_REVERSE) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_VOIDONE 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_VOIDONE", 0L);
setl(b, T_RC, 0);
userlog("[PAY_VOIDONE] 지급무효-원장역분개: 체인 완료 <- LG_REVERSE pid=%ld", cx_pid);
}
OK(b);
}

View file

@ -150,5 +150,73 @@ void PAY_XFERLINK(TPSVCINFO *p)
Bchg(b, T_MSG, 0, "당일 지급 순액 합계가 음수 - 원장 정합 오류", 0L);
FAIL(b);
}
/* ===== 8b) 업무 체인 호출부: PAY_XFERLINK -> MG_SEND (이체연계-이체전문송신) =====
* XA 규율: tpcall 로 부르는 MG_SEND 는 별도 XA 브랜치라 PAY_XFERLINK 가 방금
* INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 체인에 필요한 값은 모두
* UBF 전문(T_*)에 실어 넘기고, 하위는 전문값을 우선 사용한다(owner-writes).
* 하위 호출 실패는 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요). */
{
long cx_rlen = 0;
long cx_pid = getl(b, T_PURCHASE_ID);
long cx_sid = getl(b, T_SETTLE_ID);
long cx_amt = getl(b, T_AMOUNT);
long cx_fee = getl(b, T_FEE);
long cx_net = getl(b, T_NET);
long cx_cnt = getl(b, T_COUNT);
long cx_grs = getl(b, T_GROSS);
long cx_samt, cx_sfee, cx_snet;
char cx_bd[16], cx_mid[64], cx_sts[16];
gets_(b, T_BIZDATE, cx_bd, sizeof(cx_bd));
gets_(b, T_MERCHANT, cx_mid, sizeof(cx_mid));
gets_(b, T_STATUS, cx_sts, sizeof(cx_sts));
if (strlen(cx_bd) != 10) strcpy(cx_bd, "2026-07-20");
if (cx_pid < 0) cx_pid = 0;
if (cx_sid < 0) cx_sid = 0;
cx_samt = (cx_amt < 0) ? -cx_amt : cx_amt;
cx_sfee = (cx_fee < 0) ? -cx_fee : cx_fee;
cx_snet = (cx_net < 0) ? 0 : cx_net;
/* 하위가 필요로 하는 값을 전문에 적재 */
Bchg(b, T_BIZDATE, 0, cx_bd, 0L);
if (cx_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx_mid, 0L);
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_SETTLE_ID, cx_sid);
setl(b, T_AMOUNT, cx_samt);
setl(b, T_FEE, cx_sfee);
setl(b, T_NET, cx_snet);
Bchg(b, T_SVCNAME, 0, "PAY_XFERLINK", 0L);
Bchg(b, T_REASON, 0, "이체연계-이체전문송신", 0L);
{
char cx_ch[16];
gets_(b, T_CHANNEL, cx_ch, sizeof(cx_ch));
if (strlen(cx_ch) < 2) Bchg(b, T_CHANNEL, 0, "CH01", 0L);
}
Bchg(b, T_STR1, 0, "0200", 0L);
userlog("[PAY_XFERLINK] 이체연계-이체전문송신: 체인 호출 -> MG_SEND pid=%ld sid=%ld amt=%ld biz=%s",
cx_pid, cx_sid, cx_samt, cx_bd);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &cx_rlen, 0L) < 0) {
userlog("[PAY_XFERLINK] tpcall(MG_SEND) 실패: %s", tpstrerror(tperrno));
setl(b, T_RC, 8835);
Bchg(b, T_MSG, 0, "이체 전문송신(MG_SEND) 실패", 0L);
FAIL(b);
}
/* 하위 응답 수신 — b 가 재할당될 수 있으므로 이후 Bget/Bchg 는 갱신된 b 사용.
* PAY_XFERLINK 자신의 응답 계약(출력 필드)은 하위 응답에 덮이지 않도록 복원한다. */
setl(b, T_PURCHASE_ID, cx_pid);
setl(b, T_AMOUNT, cx_amt);
setl(b, T_FEE, cx_fee);
setl(b, T_NET, cx_net);
setl(b, T_COUNT, cx_cnt);
setl(b, T_GROSS, cx_grs);
if (cx_sid > 0) setl(b, T_SETTLE_ID, cx_sid);
if (cx_sts[0] != 0) Bchg(b, T_STATUS, 0, cx_sts, 0L);
Bchg(b, T_SVCNAME, 0, "PAY_XFERLINK", 0L);
setl(b, T_RC, 0);
userlog("[PAY_XFERLINK] 이체연계-이체전문송신: 체인 완료 <- MG_SEND pid=%ld", cx_pid);
}
OK(b);
}