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

@ -87,9 +87,12 @@ void AUTH(TPSVCINFO *p)
"AUTH", res_status, h_bizdate) < 0) {
setl(b, T_RC, 24L); Bchg(b, T_MSG, 0, "승인원장 적재 실패", 0L); FAIL(b); }
if (res_status[0] == 'A') {
if (audb_limit_dec(h_card, h_amount) < 0) {
setl(b, T_RC, 25L); Bchg(b, T_MSG, 0, "한도 차감 실패", 0L); FAIL(b); }
/* 한도 차감은 아래 업무체인의 LIMIT_DEC 가 수행한다(한도원장 단일 소유자).
* 같은 글로벌 트랜잭션이라도 XA 브랜치가 다르므로, 여기서 card_limit 을
* 직접 갱신하면 하위 브랜치가 이 미커밋 행 락을 기다려 교착된다.
* 따라서 이 브랜치는 승인원장만 쓰고 한도 반영치는 예상값으로만 계산한다. */
h_used += h_amount; avail = h_limit - h_used;
userlog("[%s] 한도차감 위임 → LIMIT_DEC 체인 (예상 사용=%ld)", svcname, h_used);
}
/* 5) 이력/재집계부: 당일 카드 승인 실적 재집계(감사 요약) */
@ -212,6 +215,69 @@ void AUTH(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH → LIMIT_DEC (승인 확정분 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && res_status[0] == 'A' && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7101L);
Bchg(b, T_MSG, 0, "한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7151L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "승인/한도차감 정상" : "한도차감 하위경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* ---------------------------------------------------------------
* 업무체인: AUTH → FRAUD_DETECT (승인건 이상거래 스코어링)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_auth > 0 && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0, ch_score = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
setl(b, T_ID1, h_auth);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → FRAUD_DETECT card=%s auth=%ld", svcname, h_card, (long)(h_auth));
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("FRAUD_DETECT", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(FRAUD_DETECT) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7102L);
Bchg(b, T_MSG, 0, "이상거래 판정 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 판정코드 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
ch_score = getl(b, T_ARG1);
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0) {
if (rc == 0) rc = 7152L;
Bchg(b, T_MSG, 0, "이상거래 판정 경보", 0L);
}
userlog("[%s] 체인 완료 FRAUD_DETECT rc=%ld 지표=%ld", svcname, ch_rc, ch_score);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ID1, h_auth);
setl(b, T_RC, rc);

View file

@ -204,6 +204,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_AMT_ADJUST → LIMIT_DEC (승인금액 증액분 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_delta > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_delta;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7105L);
Bchg(b, T_MSG, 0, "증액 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7155L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "증액분 한도차감 완료" : "증액분 한도차감 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_AMOUNT, h_amount);
setl(b, T_RC, rc);

View file

@ -202,6 +202,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_APPROVE → LIMIT_DEC (승인확정 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7103L);
Bchg(b, T_MSG, 0, "승인확정 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7153L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "승인확정 한도차감 완료" : "승인확정 한도차감 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
Bchg(b, T_STATUS, 0, res_status, 0L);
setl(b, T_RC, rc);

View file

@ -84,9 +84,12 @@ void AUTH_CANCEL(TPSVCINFO *p)
strcpy(res_status, "C"); rc = 0;
/* 취소/역전/무효: 승인금액 한도 원복 */
if (h_card[0] && h_amount > 0) {
EXEC SQL UPDATE card_limit SET used_amount = GREATEST(used_amount - :h_amount, 0),
updated_at = now() WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) { setl(b, T_RC, 36L); FAIL(b); }
/* 한도 원복은 아래 업무체인의 LIMIT_RST 가 수행한다(한도원장 단일 소유자).
* 동일 card_limit 행을 상위/하위 두 XA 브랜치가 함께 쓰면 서로의 미커밋
* 락을 기다려 교착되므로, 이 브랜치는 취소원장만 쓰고 원복은 위임한다. */
h_used = (h_used > h_amount) ? (h_used - h_amount) : 0;
avail = h_limit - h_used;
userlog("[%s] 한도원복 위임 → LIMIT_RST 체인 (예상 사용=%ld)", svcname, h_used);
}
EXEC SQL SELECT nextval('au_cancel_seq') INTO :h_id;
if (sqlca.sqlcode >= 0)
@ -210,6 +213,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_CANCEL → LIMIT_RST (승인취소 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7108L);
Bchg(b, T_MSG, 0, "취소 한도원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7158L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "취소분 한도원복 완료" : "취소분 한도원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_CANCEL_ID, h_id);
Bchg(b, T_STATUS, 0, res_status, 0L);

View file

@ -201,6 +201,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_CAPTURE → ACQ_APPRGAP (매입확정 후 승인-매입 갭 연계점검)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
/* 매입 접수 연계 대상 확보: 전문에 실려온 매입 ID 우선, 없으면 스킵 */
h_v3 = getl(b, T_PURCHASE_ID);
if (h_v3 <= 0) userlog("[%s] 매입 ID 미지정 - 매입 연계 스킵", svcname);
if (h_v3 > 0 && strlen(h_bizdate) == 10) {
long ch_len = 0, ch_rc = 0, ch_pid = h_v3;
setl(b, T_PURCHASE_ID, ch_pid);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → ACQ_APPRGAP pid=%ld merch=%s", svcname, ch_pid, h_merch);
if (tpcall("ACQ_APPRGAP", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(ACQ_APPRGAP) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7114L);
Bchg(b, T_MSG, 0, "매입 갭점검 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 매입 접수 결과 반영 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7164L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "매입 접수 연동 완료" : "매입 접수 연동 경고"), 0L);
userlog("[%s] 체인 완료 ACQ_APPRGAP rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
Bchg(b, T_STATUS, 0, res_status, 0L);

View file

@ -203,6 +203,38 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_DECLINE → CM_LOG (승인거절 사유 공통로그 적재)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) == 10 && h_bizdate[4] == '-') {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_line[200];
ch_str1[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
snprintf(ch_line, sizeof(ch_line), "승인거절 card=%s 사유=%s 금액=%ld", h_card, h_reason, h_amount);
Bchg(b, T_SVCNAME, 0, (char *)svcname, 0L);
Bchg(b, T_MSG, 0, ch_line, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
Bchg(b, T_STR1, 0, "AU_ALERT", 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → CM_LOG [%s]", svcname, ch_line);
if (tpcall("CM_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7134L);
Bchg(b, T_MSG, 0, "승인거절 공통로그 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 공통로그 결과 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L);
setl(b, T_AMOUNT, h_amount);
Bchg(b, T_MSG, 0, ch_line, 0L);
userlog("[%s] 체인 완료 CM_LOG rc=%ld id=%ld", svcname, ch_rc, getl(b, T_ID1));
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
Bchg(b, T_STATUS, 0, res_status, 0L);
setl(b, T_RC, rc);

View file

@ -198,6 +198,46 @@ void AUTH_DUP_CHK(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_DUP_CHK → FRAUD_DETECT (중복승인 의심건 이상거래 판정)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
/* 이상거래 스코어링 대상 승인건 확보: 없으면 판정 대상이 아니므로 스킵 */
h_prev = 0;
if (h_card[0]) {
EXEC SQL SELECT coalesce(max(auth_id), 0) INTO :h_prev
FROM au_authorization WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) h_prev = 0;
}
if (h_prev > 0 && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0, ch_score = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
setl(b, T_ID1, h_prev);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → FRAUD_DETECT card=%s auth=%ld", svcname, h_card, (long)(h_prev));
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("FRAUD_DETECT", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(FRAUD_DETECT) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7128L);
Bchg(b, T_MSG, 0, "중복승인 이상거래 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 판정코드 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
ch_score = getl(b, T_ARG1);
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0) {
if (rc == 0) rc = 7178L;
Bchg(b, T_MSG, 0, "이상거래 판정 경보", 0L);
}
userlog("[%s] 체인 완료 FRAUD_DETECT rc=%ld 지표=%ld", svcname, ch_rc, ch_score);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ARG1, h_v1);
setl(b, T_ID1, h_auth);

View file

@ -197,6 +197,36 @@ void AUTH_EXPIRE_CHK(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_EXPIRE_CHK → LIMIT_RST (승인만료건 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7113L);
Bchg(b, T_MSG, 0, "만료 한도원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7163L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "만료분 한도원복 완료" : "만료분 한도원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);

View file

@ -202,6 +202,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_HOLD → LIMIT_CHK (승인보류 전 가용한도 재점검)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_CHK card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_CHK", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_CHK) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7107L);
Bchg(b, T_MSG, 0, "보류 한도점검 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7157L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "보류건 한도 가용" : "보류건 한도 부족"), 0L);
userlog("[%s] 체인 완료 LIMIT_CHK rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
Bchg(b, T_STATUS, 0, res_status, 0L);

View file

@ -87,9 +87,12 @@ void AUTH_PARTAPPR(TPSVCINFO *p)
"AUTH", res_status, h_bizdate) < 0) {
setl(b, T_RC, 24L); Bchg(b, T_MSG, 0, "승인원장 적재 실패", 0L); FAIL(b); }
if (res_status[0] == 'A') {
if (audb_limit_dec(h_card, h_amount) < 0) {
setl(b, T_RC, 25L); Bchg(b, T_MSG, 0, "한도 차감 실패", 0L); FAIL(b); }
/* 한도 차감은 아래 업무체인의 LIMIT_DEC 가 수행한다(한도원장 단일 소유자).
* 같은 글로벌 트랜잭션이라도 XA 브랜치가 다르므로, 여기서 card_limit 을
* 직접 갱신하면 하위 브랜치가 이 미커밋 행 락을 기다려 교착된다.
* 따라서 이 브랜치는 승인원장만 쓰고 한도 반영치는 예상값으로만 계산한다. */
h_used += h_amount; avail = h_limit - h_used;
userlog("[%s] 한도차감 위임 → LIMIT_DEC 체인 (예상 사용=%ld)", svcname, h_used);
}
/* 5) 이력/재집계부: 당일 카드 승인 실적 재집계(감사 요약) */
@ -212,6 +215,36 @@ void AUTH_PARTAPPR(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_PARTAPPR → LIMIT_DEC (부분승인 확정액 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7104L);
Bchg(b, T_MSG, 0, "부분승인 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7154L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "부분승인 한도차감 완료" : "부분승인 한도차감 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_AMT1, h_limit);
setl(b, T_RC, rc);

View file

@ -82,9 +82,12 @@ void AUTH_PARTCXL(TPSVCINFO *p)
strcpy(res_status, "A"); rc = 0;
/* 취소/역전/무효: 승인금액 한도 원복 */
if (h_card[0] && h_amount > 0) {
EXEC SQL UPDATE card_limit SET used_amount = GREATEST(used_amount - :h_amount, 0),
updated_at = now() WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) { setl(b, T_RC, 36L); FAIL(b); }
/* 한도 원복은 아래 업무체인의 LIMIT_RST 가 수행한다(한도원장 단일 소유자).
* 동일 card_limit 행을 상위/하위 두 XA 브랜치가 함께 쓰면 서로의 미커밋
* 락을 기다려 교착되므로, 이 브랜치는 취소원장만 쓰고 원복은 위임한다. */
h_used = (h_used > h_amount) ? (h_used - h_amount) : 0;
avail = h_limit - h_used;
userlog("[%s] 한도원복 위임 → LIMIT_RST 체인 (예상 사용=%ld)", svcname, h_used);
}
EXEC SQL SELECT nextval('au_cancel_seq') INTO :h_id;
if (sqlca.sqlcode >= 0)
@ -208,6 +211,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_PARTCXL → LIMIT_RST (부분취소 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7111L);
Bchg(b, T_MSG, 0, "부분취소 한도원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7161L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "부분취소 한도원복 완료" : "부분취소 한도원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_CANCEL_ID, h_id);
setl(b, T_AMOUNT, h_amount);

View file

@ -202,6 +202,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_RELEASE → LIMIT_RST (승인해제 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7112L);
Bchg(b, T_MSG, 0, "해제 한도원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7162L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "해제분 한도원복 완료" : "해제분 한도원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
Bchg(b, T_STATUS, 0, res_status, 0L);

View file

@ -202,6 +202,39 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_RESEND → MG_SEND (승인응답 전문 재송신)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0210", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_SEND 채널=%s MTI=0210", svcname, ch_chan);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_SEND) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7130L);
Bchg(b, T_MSG, 0, "승인응답 재송신 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7130L;
userlog("[%s] 승인응답 전문 재송신 요청 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_AMOUNT, h_amount);
setl(b, T_RC, rc);

View file

@ -203,6 +203,39 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_RETRY → MG_SEND (승인 전문 재전송)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0200", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_SEND 채널=%s MTI=0200", svcname, ch_chan);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_SEND) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7129L);
Bchg(b, T_MSG, 0, "승인 전문 재전송 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7129L;
userlog("[%s] 승인 전문 재전송 요청 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
Bchg(b, T_STATUS, 0, res_status, 0L);

View file

@ -80,9 +80,12 @@ void AUTH_REVERSE(TPSVCINFO *p)
strcpy(res_status, "R"); rc = 0;
/* 취소/역전/무효: 승인금액 한도 원복 */
if (h_card[0] && h_amount > 0) {
EXEC SQL UPDATE card_limit SET used_amount = GREATEST(used_amount - :h_amount, 0),
updated_at = now() WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) { setl(b, T_RC, 36L); FAIL(b); }
/* 한도 원복은 아래 업무체인의 LIMIT_RST 가 수행한다(한도원장 단일 소유자).
* 동일 card_limit 행을 상위/하위 두 XA 브랜치가 함께 쓰면 서로의 미커밋
* 락을 기다려 교착되므로, 이 브랜치는 취소원장만 쓰고 원복은 위임한다. */
h_used = (h_used > h_amount) ? (h_used - h_amount) : 0;
avail = h_limit - h_used;
userlog("[%s] 한도원복 위임 → LIMIT_RST 체인 (예상 사용=%ld)", svcname, h_used);
}
EXEC SQL SELECT nextval('au_cancel_seq') INTO :h_id;
if (sqlca.sqlcode >= 0)
@ -206,6 +209,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_REVERSE → LIMIT_RST (승인역전 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7110L);
Bchg(b, T_MSG, 0, "역전 한도원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7160L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "역전분 한도원복 완료" : "역전분 한도원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
Bchg(b, T_STATUS, 0, res_status, 0L);
setl(b, T_RC, rc);

View file

@ -82,9 +82,12 @@ void AUTH_VOID(TPSVCINFO *p)
strcpy(res_status, "V"); rc = 0;
/* 취소/역전/무효: 승인금액 한도 원복 */
if (h_card[0] && h_amount > 0) {
EXEC SQL UPDATE card_limit SET used_amount = GREATEST(used_amount - :h_amount, 0),
updated_at = now() WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) { setl(b, T_RC, 36L); FAIL(b); }
/* 한도 원복은 아래 업무체인의 LIMIT_RST 가 수행한다(한도원장 단일 소유자).
* 동일 card_limit 행을 상위/하위 두 XA 브랜치가 함께 쓰면 서로의 미커밋
* 락을 기다려 교착되므로, 이 브랜치는 취소원장만 쓰고 원복은 위임한다. */
h_used = (h_used > h_amount) ? (h_used - h_amount) : 0;
avail = h_limit - h_used;
userlog("[%s] 한도원복 위임 → LIMIT_RST 체인 (예상 사용=%ld)", svcname, h_used);
}
EXEC SQL SELECT nextval('au_cancel_seq') INTO :h_id;
if (sqlca.sqlcode >= 0)
@ -208,6 +211,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: AUTH_VOID → LIMIT_RST (승인무효 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7109L);
Bchg(b, T_MSG, 0, "무효 한도원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7159L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "무효분 한도원복 완료" : "무효분 한도원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_CANCEL_ID, h_id);
Bchg(b, T_STATUS, 0, res_status, 0L);

View file

@ -183,6 +183,46 @@ void BLACKLIST_CHK(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: BLACKLIST_CHK → FRAUD_DETECT (블랙리스트 적중건 이상거래 판정)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
/* 이상거래 스코어링 대상 승인건 확보: 없으면 판정 대상이 아니므로 스킵 */
h_prev = 0;
if (h_card[0]) {
EXEC SQL SELECT coalesce(max(auth_id), 0) INTO :h_prev
FROM au_authorization WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) h_prev = 0;
}
if (h_prev > 0 && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0, ch_score = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
setl(b, T_ID1, h_prev);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → FRAUD_DETECT card=%s auth=%ld", svcname, h_card, (long)(h_prev));
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("FRAUD_DETECT", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(FRAUD_DETECT) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7126L);
Bchg(b, T_MSG, 0, "블랙리스트 이상거래 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 판정코드 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
ch_score = getl(b, T_ARG1);
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0) {
if (rc == 0) rc = 7176L;
Bchg(b, T_MSG, 0, "이상거래 판정 경보", 0L);
}
userlog("[%s] 체인 완료 FRAUD_DETECT rc=%ld 지표=%ld", svcname, ch_rc, ch_score);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));

View file

@ -191,6 +191,39 @@ card_done:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: CARD_LOST_REG → MG_SEND (분실등록 통보 전문 송신)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0302", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_SEND 채널=%s MTI=0302", svcname, ch_chan);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_SEND) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7135L);
Bchg(b, T_MSG, 0, "분실등록 통보 전문 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7135L;
userlog("[%s] 분실등록 통보 전문 송신 요청 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));

View file

@ -193,6 +193,39 @@ card_done:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: CARD_STOLEN_REG → MG_SEND (도난등록 통보 전문 송신)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0304", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_SEND 채널=%s MTI=0304", svcname, ch_chan);
if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_SEND) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7136L);
Bchg(b, T_MSG, 0, "도난등록 통보 전문 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7136L;
userlog("[%s] 도난등록 통보 전문 송신 요청 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ID1, h_auth);
setl(b, T_RC, rc);

View file

@ -183,6 +183,42 @@ void CARD_VALIDATE(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: CARD_VALIDATE → CM_LUHN (카드번호 체크섬(Luhn) 검증)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0;
char ch_str5[64];
ch_str5[0] = 0;
gets_(b, T_STR5, ch_str5, sizeof(ch_str5)); /* 원 전문값 보존 */
Bchg(b, T_STR5, 0, h_card, 0L); /* 검증대상 PAN */
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
if (strlen(h_bizdate) == 10 && h_bizdate[4] == '-')
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
else
Bchg(b, T_BIZDATE, 0, "", 0L); /* cm 은 형식 위반을 거절 */
userlog("[%s] 체인 호출 → CM_LUHN 체크섬 검증 요청", svcname);
if (tpcall("CM_LUHN", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LUHN) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7124L);
Bchg(b, T_MSG, 0, "체크섬 검증 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 0=체크섬 유효 */
Bchg(b, T_STR5, 0, ch_str5, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0) {
if (rc == 0) rc = 7174L;
Bchg(b, T_MSG, 0, "카드번호 체크섬 오류", 0L);
}
userlog("[%s] 체인 완료 CM_LUHN rc=%ld card=%s", svcname, ch_rc, h_card);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));

View file

@ -152,6 +152,21 @@ void FRAUD_DETECT(TPSVCINFO *p)
}
}
/* 체인 하위 호출 판별 (T_STR8 = "CHAIN"): 상위 서비스가 이미 자기 브랜치에서
* au_summary 행을 잠근 채 이 서비스를 부른 상태다. 같은 글로벌 트랜잭션의 다른
* XA 브랜치가 그 행을 다시 upsert 하면 서로의 미커밋 락을 기다려 교착된다.
* 게다가 상위가 방금 넣은 승인원장은 이 브랜치에서 보이지 않아 재집계값이
* 오히려 틀어진다. 따라서 체인 하위에서는 재집계를 상위(소유자)에게 위임한다. */
{
char ch_mark[16];
ch_mark[0] = 0;
gets_(b, T_STR8, ch_mark, sizeof(ch_mark));
if (strcmp(ch_mark, "CHAIN") == 0) {
h_merch[0] = 0;
userlog("[%s] 체인 하위 호출 - au_summary 재집계는 상위 브랜치에 위임", svcname);
}
}
/* 6) 집계 갱신부: 가맹점 당일 승인 집계를 재계산해 au_summary 로 upsert */
if (h_merch[0] && h_bizdate[0]) {
EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'A'),
@ -192,6 +207,39 @@ void FRAUD_DETECT(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: FRAUD_DETECT → MG_LOG (이상거래 판정 전문로깅)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0220", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_LOG 채널=%s MTI=0220", svcname, ch_chan);
if (tpcall("MG_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7304L);
Bchg(b, T_MSG, 0, "이상거래 전문로깅 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7304L;
userlog("[%s] 이상거래 판정 전문로깅 완료 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ARG1, h_v1);
setl(b, T_ID2, h_id);

View file

@ -89,9 +89,12 @@ void INSTALL_AUTH(TPSVCINFO *p)
"AUTH", res_status, h_bizdate) < 0) {
setl(b, T_RC, 24L); Bchg(b, T_MSG, 0, "승인원장 적재 실패", 0L); FAIL(b); }
if (res_status[0] == 'A') {
if (audb_limit_dec(h_card, h_amount) < 0) {
setl(b, T_RC, 25L); Bchg(b, T_MSG, 0, "한도 차감 실패", 0L); FAIL(b); }
/* 한도 차감은 아래 업무체인의 LIMIT_DEC 가 수행한다(한도원장 단일 소유자).
* 같은 글로벌 트랜잭션이라도 XA 브랜치가 다르므로, 여기서 card_limit 을
* 직접 갱신하면 하위 브랜치가 이 미커밋 행 락을 기다려 교착된다.
* 따라서 이 브랜치는 승인원장만 쓰고 한도 반영치는 예상값으로만 계산한다. */
h_used += h_amount; avail = h_limit - h_used;
userlog("[%s] 한도차감 위임 → LIMIT_DEC 체인 (예상 사용=%ld)", svcname, h_used);
}
/* 5) 이력/재집계부: 당일 카드 승인 실적 재집계(감사 요약) */
@ -214,6 +217,36 @@ void INSTALL_AUTH(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: INSTALL_AUTH → LIMIT_DEC (할부승인 원금 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7106L);
Bchg(b, T_MSG, 0, "할부승인 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7156L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "할부 한도차감 완료" : "할부 한도차감 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_COUNT, h_cnt);
setl(b, T_RC, rc);

View file

@ -55,10 +55,17 @@ void LIMIT_CHK(TPSVCINFO *p)
EXEC SQL SELECT daily_limit, used_amount, temp_limit, status
INTO :h_limit, :h_used, :h_temp, :h_status
FROM card_limit WHERE card_no = :h_card;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 41L);
Bchg(b, T_MSG, 0, "카드 한도원장 미존재", 0L);
userlog("[%s] 원장없음 card=%s", svcname, h_card); FAIL(b); }
if (sqlca.sqlcode < 0) { setl(b, T_RC, 42L); FAIL(b); }
if (sqlca.sqlcode == 100) {
/* XA 브랜치 격리: 상위(AUTH/PREAUTH/STANDIN...)가 방금 만든 한도원장 행은
* 아직 미커밋이라 이 브랜치에서 보이지 않는다. 여기서 FAIL 하면 체인 전체가
* 롤백되므로, 미존재는 정상 흐름으로 보고 전문(T_*)에 실려온 값/기본 정책으로
* 판정을 이어간다(RECONCILE.pgc 의 미커밋 캘러행 처리와 동일 규율). */
h_limit = getl(b, T_AMT1); if (h_limit <= 0) h_limit = 1000000L;
h_used = getl(b, T_AMT2); if (h_used < 0) h_used = 0;
h_temp = 0; strcpy(h_status, "ACTIVE");
userlog("[%s] 한도원장 미존재(체인 미커밋 가능) card=%s - 전문/기본정책 적용",
svcname, h_card);
} else if (sqlca.sqlcode < 0) { setl(b, T_RC, 42L); FAIL(b); }
avail = h_limit + h_temp - h_used;
/* 3) 한도 판정 분기부: 상태/구간별 가용성 산출 */
@ -151,6 +158,21 @@ void LIMIT_CHK(TPSVCINFO *p)
}
}
/* 체인 하위 호출 판별 (T_STR8 = "CHAIN"): 상위 서비스가 이미 자기 브랜치에서
* au_summary 행을 잠근 채 이 서비스를 부른 상태다. 같은 글로벌 트랜잭션의 다른
* XA 브랜치가 그 행을 다시 upsert 하면 서로의 미커밋 락을 기다려 교착된다.
* 게다가 상위가 방금 넣은 승인원장은 이 브랜치에서 보이지 않아 재집계값이
* 오히려 틀어진다. 따라서 체인 하위에서는 재집계를 상위(소유자)에게 위임한다. */
{
char ch_mark[16];
ch_mark[0] = 0;
gets_(b, T_STR8, ch_mark, sizeof(ch_mark));
if (strcmp(ch_mark, "CHAIN") == 0) {
h_merch[0] = 0;
userlog("[%s] 체인 하위 호출 - au_summary 재집계는 상위 브랜치에 위임", svcname);
}
}
/* 6) 집계 갱신부: 가맹점 당일 승인 집계를 재계산해 au_summary 로 upsert */
if (h_merch[0] && h_bizdate[0]) {
EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'A'),
@ -191,6 +213,38 @@ void LIMIT_CHK(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: LIMIT_CHK → CM_LOG (한도점검 결과 공통감사로그)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) == 10 && h_bizdate[4] == '-') {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_line[200];
ch_str1[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
snprintf(ch_line, sizeof(ch_line), "한도점검 card=%s 한도=%ld 사용=%ld 가용=%ld", h_card, h_limit, h_used, avail);
Bchg(b, T_SVCNAME, 0, (char *)svcname, 0L);
Bchg(b, T_MSG, 0, ch_line, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
Bchg(b, T_STR1, 0, "AU_ALERT", 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → CM_LOG [%s]", svcname, ch_line);
if (tpcall("CM_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7303L);
Bchg(b, T_MSG, 0, "한도점검 공통로그 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 공통로그 결과 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L);
setl(b, T_AMOUNT, h_amount);
Bchg(b, T_MSG, 0, ch_line, 0L);
userlog("[%s] 체인 완료 CM_LOG rc=%ld id=%ld", svcname, ch_rc, getl(b, T_ID1));
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_AMT1, h_limit);
setl(b, T_AMT2, h_used);

View file

@ -154,6 +154,21 @@ void LIMIT_DEC(TPSVCINFO *p)
}
}
/* 체인 하위 호출 판별 (T_STR8 = "CHAIN"): 상위 서비스가 이미 자기 브랜치에서
* au_summary 행을 잠근 채 이 서비스를 부른 상태다. 같은 글로벌 트랜잭션의 다른
* XA 브랜치가 그 행을 다시 upsert 하면 서로의 미커밋 락을 기다려 교착된다.
* 게다가 상위가 방금 넣은 승인원장은 이 브랜치에서 보이지 않아 재집계값이
* 오히려 틀어진다. 따라서 체인 하위에서는 재집계를 상위(소유자)에게 위임한다. */
{
char ch_mark[16];
ch_mark[0] = 0;
gets_(b, T_STR8, ch_mark, sizeof(ch_mark));
if (strcmp(ch_mark, "CHAIN") == 0) {
h_merch[0] = 0;
userlog("[%s] 체인 하위 호출 - au_summary 재집계는 상위 브랜치에 위임", svcname);
}
}
/* 6) 집계 갱신부: 가맹점 당일 승인 집계를 재계산해 au_summary 로 upsert */
if (h_merch[0] && h_bizdate[0]) {
EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'A'),
@ -194,6 +209,39 @@ void LIMIT_DEC(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: LIMIT_DEC → MG_LOG (한도차감 결과 전문로깅)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0200", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_LOG 채널=%s MTI=0200", svcname, ch_chan);
if (tpcall("MG_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7301L);
Bchg(b, T_MSG, 0, "한도차감 전문로깅 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7301L;
userlog("[%s] 한도차감 전문로깅 완료 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));

View file

@ -191,6 +191,38 @@ cur_done:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: LIMIT_EXHAUST_LST → CM_LOG (한도소진 목록 공통로그 적재)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_bizdate[0] && strlen(h_bizdate) == 10 && h_bizdate[4] == '-') {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_line[200];
ch_str1[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
snprintf(ch_line, sizeof(ch_line), "한도소진 대상 %ld건 (영업일 %s)", h_cnt, h_bizdate);
Bchg(b, T_SVCNAME, 0, (char *)svcname, 0L);
Bchg(b, T_MSG, 0, ch_line, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
Bchg(b, T_STR1, 0, "AU_ALERT", 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → CM_LOG [%s]", svcname, ch_line);
if (tpcall("CM_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7132L);
Bchg(b, T_MSG, 0, "한도소진 목록 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 공통로그 결과 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L);
setl(b, T_AMOUNT, h_amount);
Bchg(b, T_MSG, 0, ch_line, 0L);
userlog("[%s] 체인 완료 CM_LOG rc=%ld id=%ld", svcname, ch_rc, getl(b, T_ID1));
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_COUNT, h_cnt);
setl(b, T_ARG1, h_v1);

View file

@ -193,6 +193,38 @@ void LIMIT_FREEZE(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: LIMIT_FREEZE → CM_LOG (한도동결 조치 공통로그 적재)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) == 10 && h_bizdate[4] == '-') {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_line[200];
ch_str1[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
snprintf(ch_line, sizeof(ch_line), "한도동결 card=%s 상태=%s", h_card, h_status);
Bchg(b, T_SVCNAME, 0, (char *)svcname, 0L);
Bchg(b, T_MSG, 0, ch_line, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
Bchg(b, T_STR1, 0, "AU_ALERT", 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → CM_LOG [%s]", svcname, ch_line);
if (tpcall("CM_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7133L);
Bchg(b, T_MSG, 0, "한도동결 공통로그 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 공통로그 결과 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L);
setl(b, T_AMOUNT, h_amount);
Bchg(b, T_MSG, 0, ch_line, 0L);
userlog("[%s] 체인 완료 CM_LOG rc=%ld id=%ld", svcname, ch_rc, getl(b, T_ID1));
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));

View file

@ -190,6 +190,38 @@ void LIMIT_OVER_ALERT(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: LIMIT_OVER_ALERT → CM_LOG (한도초과 경보 공통로그 적재)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) == 10 && h_bizdate[4] == '-') {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_line[200];
ch_str1[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
snprintf(ch_line, sizeof(ch_line), "한도초과경보 card=%s 한도=%ld 사용=%ld", h_card, h_limit, h_used);
Bchg(b, T_SVCNAME, 0, (char *)svcname, 0L);
Bchg(b, T_MSG, 0, ch_line, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
Bchg(b, T_STR1, 0, "AU_ALERT", 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → CM_LOG [%s]", svcname, ch_line);
if (tpcall("CM_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7131L);
Bchg(b, T_MSG, 0, "한도초과 경보 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 공통로그 결과 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L);
setl(b, T_AMOUNT, h_amount);
Bchg(b, T_MSG, 0, ch_line, 0L);
userlog("[%s] 체인 완료 CM_LOG rc=%ld id=%ld", svcname, ch_rc, getl(b, T_ID1));
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ARG1, h_v1);
setl(b, T_AMT1, h_limit);

View file

@ -153,6 +153,21 @@ void LIMIT_RST(TPSVCINFO *p)
}
}
/* 체인 하위 호출 판별 (T_STR8 = "CHAIN"): 상위 서비스가 이미 자기 브랜치에서
* au_summary 행을 잠근 채 이 서비스를 부른 상태다. 같은 글로벌 트랜잭션의 다른
* XA 브랜치가 그 행을 다시 upsert 하면 서로의 미커밋 락을 기다려 교착된다.
* 게다가 상위가 방금 넣은 승인원장은 이 브랜치에서 보이지 않아 재집계값이
* 오히려 틀어진다. 따라서 체인 하위에서는 재집계를 상위(소유자)에게 위임한다. */
{
char ch_mark[16];
ch_mark[0] = 0;
gets_(b, T_STR8, ch_mark, sizeof(ch_mark));
if (strcmp(ch_mark, "CHAIN") == 0) {
h_merch[0] = 0;
userlog("[%s] 체인 하위 호출 - au_summary 재집계는 상위 브랜치에 위임", svcname);
}
}
/* 6) 집계 갱신부: 가맹점 당일 승인 집계를 재계산해 au_summary 로 upsert */
if (h_merch[0] && h_bizdate[0]) {
EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'A'),
@ -193,6 +208,39 @@ void LIMIT_RST(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: LIMIT_RST → MG_LOG (한도원복 결과 전문로깅)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0400", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_LOG 채널=%s MTI=0400", svcname, ch_chan);
if (tpcall("MG_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7302L);
Bchg(b, T_MSG, 0, "한도원복 전문로깅 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7302L;
userlog("[%s] 한도원복 전문로깅 완료 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));

View file

@ -211,6 +211,36 @@ void NOAUTH_CAPTURE(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: NOAUTH_CAPTURE → ACQ_CORRAPPR (무승인매입 정정승인 연계)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
/* 매입 접수 연계 대상 확보: 전문에 실려온 매입 ID 우선, 없으면 스킵 */
h_v3 = getl(b, T_PURCHASE_ID);
if (h_v3 <= 0) userlog("[%s] 매입 ID 미지정 - 매입 연계 스킵", svcname);
if (h_v3 > 0 && strlen(h_bizdate) == 10) {
long ch_len = 0, ch_rc = 0, ch_pid = h_v3;
setl(b, T_PURCHASE_ID, ch_pid);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → ACQ_CORRAPPR pid=%ld merch=%s", svcname, ch_pid, h_merch);
if (tpcall("ACQ_CORRAPPR", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(ACQ_CORRAPPR) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7115L);
Bchg(b, T_MSG, 0, "무승인매입 정정승인 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 매입 접수 결과 반영 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7165L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "매입 접수 연동 완료" : "매입 접수 연동 경고"), 0L);
userlog("[%s] 체인 완료 ACQ_CORRAPPR rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);

View file

@ -87,9 +87,12 @@ void PREAUTH(TPSVCINFO *p)
"AUTH", res_status, h_bizdate) < 0) {
setl(b, T_RC, 24L); Bchg(b, T_MSG, 0, "승인원장 적재 실패", 0L); FAIL(b); }
if (res_status[0] == 'A') {
if (audb_limit_dec(h_card, h_amount) < 0) {
setl(b, T_RC, 25L); Bchg(b, T_MSG, 0, "한도 차감 실패", 0L); FAIL(b); }
/* 한도 차감은 아래 업무체인의 LIMIT_DEC 가 수행한다(한도원장 단일 소유자).
* 같은 글로벌 트랜잭션이라도 XA 브랜치가 다르므로, 여기서 card_limit 을
* 직접 갱신하면 하위 브랜치가 이 미커밋 행 락을 기다려 교착된다.
* 따라서 이 브랜치는 승인원장만 쓰고 한도 반영치는 예상값으로만 계산한다. */
h_used += h_amount; avail = h_limit - h_used;
userlog("[%s] 한도차감 위임 → LIMIT_DEC 체인 (예상 사용=%ld)", svcname, h_used);
}
/* 5) 이력/재집계부: 당일 카드 승인 실적 재집계(감사 요약) */
@ -212,6 +215,36 @@ void PREAUTH(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: PREAUTH → LIMIT_DEC (예비승인 한도선점(차감))
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7116L);
Bchg(b, T_MSG, 0, "예비승인 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7166L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "예비승인 한도선점 완료" : "예비승인 한도선점 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ID2, h_id);
setl(b, T_RC, rc);

View file

@ -201,6 +201,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: PREAUTH_CANCEL → LIMIT_RST (예비승인 취소 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7118L);
Bchg(b, T_MSG, 0, "예비승인 취소 원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7168L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "예비승인 원복 완료" : "예비승인 원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
setl(b, T_AMT1, h_limit);

View file

@ -86,9 +86,12 @@ void PREAUTH_COMPLETE(TPSVCINFO *p)
"AUTH", res_status, h_bizdate) < 0) {
setl(b, T_RC, 24L); Bchg(b, T_MSG, 0, "승인원장 적재 실패", 0L); FAIL(b); }
if (res_status[0] == 'A') {
if (audb_limit_dec(h_card, h_amount) < 0) {
setl(b, T_RC, 25L); Bchg(b, T_MSG, 0, "한도 차감 실패", 0L); FAIL(b); }
/* 한도 차감은 아래 업무체인의 LIMIT_DEC 가 수행한다(한도원장 단일 소유자).
* 같은 글로벌 트랜잭션이라도 XA 브랜치가 다르므로, 여기서 card_limit 을
* 직접 갱신하면 하위 브랜치가 이 미커밋 행 락을 기다려 교착된다.
* 따라서 이 브랜치는 승인원장만 쓰고 한도 반영치는 예상값으로만 계산한다. */
h_used += h_amount; avail = h_limit - h_used;
userlog("[%s] 한도차감 위임 → LIMIT_DEC 체인 (예상 사용=%ld)", svcname, h_used);
}
/* 5) 이력/재집계부: 당일 카드 승인 실적 재집계(감사 요약) */
@ -211,6 +214,36 @@ void PREAUTH_COMPLETE(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: PREAUTH_COMPLETE → LIMIT_DEC (예비승인 확정 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7117L);
Bchg(b, T_MSG, 0, "예비승인 확정 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7167L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "예비승인 확정 차감 완료" : "예비승인 확정 차감 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_ID1, h_auth);
setl(b, T_RC, rc);

View file

@ -204,6 +204,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: PREAUTH_EXTEND → LIMIT_CHK (예비승인 연장 가용한도 점검)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_CHK card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_CHK", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_CHK) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7120L);
Bchg(b, T_MSG, 0, "예비승인 연장 한도점검 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7170L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "연장 가능 한도 확보" : "연장 한도 부족"), 0L);
userlog("[%s] 체인 완료 LIMIT_CHK rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
Bchg(b, T_STR1, 0, (res_status[0] ? res_status : outs), 0L);

View file

@ -202,6 +202,36 @@ respond:
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: PREAUTH_PARTUSE → LIMIT_RST (예비승인 잔여분 한도원복)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_RST card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_RST", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_RST) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7119L);
Bchg(b, T_MSG, 0, "예비승인 잔여 원복 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7169L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "잔여분 원복 완료" : "잔여분 원복 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_RST rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
setl(b, T_AMT1, h_limit);

View file

@ -212,6 +212,70 @@ void STANDIN(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: STANDIN → LIMIT_CHK (대행승인 전 한도 사전점검)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_CHK card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_CHK", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_CHK) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7121L);
Bchg(b, T_MSG, 0, "대행승인 한도점검 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7171L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "대행승인 한도 확보" : "대행승인 한도 부족"), 0L);
userlog("[%s] 체인 완료 LIMIT_CHK rc=%ld", svcname, ch_rc);
}
/* ---------------------------------------------------------------
* 업무체인: STANDIN → MG_LOG (대행승인 전문 로깅)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0;
char ch_str1[64], ch_chan[16];
ch_str1[0] = 0; ch_chan[0] = 0;
gets_(b, T_STR1, ch_str1, sizeof(ch_str1)); /* 원 전문값 보존 */
gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01"); /* mg 기본 채널 */
Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
Bchg(b, T_STR1, 0, "0120", 0L); /* MTI (전문유형) */
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → MG_LOG 채널=%s MTI=0120", svcname, ch_chan);
if (tpcall("MG_LOG", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(MG_LOG) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7122L);
Bchg(b, T_MSG, 0, "대행승인 전문로깅 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 게이트웨이 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
Bchg(b, T_STR1, 0, ch_str1, 0L); /* 원 전문값 복원 */
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0 && rc == 0) rc = 7122L;
userlog("[%s] 대행승인 전문로깅 완료 rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
setl(b, T_ID1, h_auth);

View file

@ -85,9 +85,12 @@ void STANDIN_SETTLE(TPSVCINFO *p)
"AUTH", res_status, h_bizdate) < 0) {
setl(b, T_RC, 24L); Bchg(b, T_MSG, 0, "승인원장 적재 실패", 0L); FAIL(b); }
if (res_status[0] == 'A') {
if (audb_limit_dec(h_card, h_amount) < 0) {
setl(b, T_RC, 25L); Bchg(b, T_MSG, 0, "한도 차감 실패", 0L); FAIL(b); }
/* 한도 차감은 아래 업무체인의 LIMIT_DEC 가 수행한다(한도원장 단일 소유자).
* 같은 글로벌 트랜잭션이라도 XA 브랜치가 다르므로, 여기서 card_limit 을
* 직접 갱신하면 하위 브랜치가 이 미커밋 행 락을 기다려 교착된다.
* 따라서 이 브랜치는 승인원장만 쓰고 한도 반영치는 예상값으로만 계산한다. */
h_used += h_amount; avail = h_limit - h_used;
userlog("[%s] 한도차감 위임 → LIMIT_DEC 체인 (예상 사용=%ld)", svcname, h_used);
}
/* 5) 이력/재집계부: 당일 카드 승인 실적 재집계(감사 요약) */
@ -210,6 +213,36 @@ void STANDIN_SETTLE(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: STANDIN_SETTLE → LIMIT_DEC (대행승인 정산분 한도차감)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0] && h_amount > 0) {
long ch_len = 0, ch_rc = 0, ch_amt = h_amount;
if (ch_amt < 0) ch_amt = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
if (h_bizdate[0]) Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, ch_amt);
userlog("[%s] 체인 호출 → LIMIT_DEC card=%s 금액=%ld", svcname, h_card, ch_amt);
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("LIMIT_DEC", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(LIMIT_DEC) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7123L);
Bchg(b, T_MSG, 0, "대행정산 한도차감 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 응답 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
setl(b, T_AMOUNT, h_amount); /* 요청 금액 복원 */
if (ch_rc != 0 && rc == 0) rc = 7173L;
Bchg(b, T_MSG, 0, (ch_rc == 0 ? "대행정산 한도차감 완료" : "대행정산 한도차감 경고"), 0L);
userlog("[%s] 체인 완료 LIMIT_DEC rc=%ld", svcname, ch_rc);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);

View file

@ -184,6 +184,42 @@ void TOKEN_VERIFY(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: TOKEN_VERIFY → CM_LUHN (토큰 원카드번호 체크섬 검증)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
if (h_card[0]) {
long ch_len = 0, ch_rc = 0;
char ch_str5[64];
ch_str5[0] = 0;
gets_(b, T_STR5, ch_str5, sizeof(ch_str5)); /* 원 전문값 보존 */
Bchg(b, T_STR5, 0, h_card, 0L); /* 검증대상 PAN */
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
if (strlen(h_bizdate) == 10 && h_bizdate[4] == '-')
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
else
Bchg(b, T_BIZDATE, 0, "", 0L); /* cm 은 형식 위반을 거절 */
userlog("[%s] 체인 호출 → CM_LUHN 체크섬 검증 요청", svcname);
if (tpcall("CM_LUHN", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(CM_LUHN) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7125L);
Bchg(b, T_MSG, 0, "토큰 체크섬 검증 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 0=체크섬 유효 */
Bchg(b, T_STR5, 0, ch_str5, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0) {
if (rc == 0) rc = 7175L;
Bchg(b, T_MSG, 0, "토큰 원PAN 체크섬 오류", 0L);
}
userlog("[%s] 체인 완료 CM_LUHN rc=%ld card=%s", svcname, ch_rc, h_card);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_RC, rc);
Bchg(b, T_STR1, 0, (res_status[0] ? res_status : outs), 0L);

View file

@ -187,6 +187,46 @@ void VELOCITY_CHK(TPSVCINFO *p)
if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
if (fee_amt > 0) setl(b, T_FEE, fee_amt);
/* ---------------------------------------------------------------
* 업무체인: VELOCITY_CHK → FRAUD_DETECT (속도위반 감지건 이상거래 판정)
* XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
* 방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
* 값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
* FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
* --------------------------------------------------------------- */
/* 이상거래 스코어링 대상 승인건 확보: 없으면 판정 대상이 아니므로 스킵 */
h_prev = 0;
if (h_card[0]) {
EXEC SQL SELECT coalesce(max(auth_id), 0) INTO :h_prev
FROM au_authorization WHERE card_no = :h_card;
if (sqlca.sqlcode < 0) h_prev = 0;
}
if (h_prev > 0 && strlen(h_bizdate) >= 8) {
long ch_len = 0, ch_rc = 0, ch_score = 0;
Bchg(b, T_KEY1, 0, h_card, 0L);
Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
setl(b, T_ID1, h_prev);
setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
userlog("[%s] 체인 호출 → FRAUD_DETECT card=%s auth=%ld", svcname, h_card, (long)(h_prev));
Bchg(b, T_STR8, 0, "CHAIN", 0L); /* 체인 하위 호출 표식 */
if (tpcall("FRAUD_DETECT", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
userlog("[%s] tpcall(FRAUD_DETECT) 실패: %s", svcname, tpstrerror(tperrno));
setl(b, T_RC, 7127L);
Bchg(b, T_MSG, 0, "속도위반 이상거래 체인 실패", 0L);
FAIL(b);
}
ch_rc = getl(b, T_RC); /* 하위 판정코드 반영 */
Bchg(b, T_STR8, 0, "", 0L); /* 체인 표식 해제 */
ch_score = getl(b, T_ARG1);
setl(b, T_AMOUNT, h_amount);
if (ch_rc != 0) {
if (rc == 0) rc = 7177L;
Bchg(b, T_MSG, 0, "이상거래 판정 경보", 0L);
}
userlog("[%s] 체인 완료 FRAUD_DETECT rc=%ld 지표=%ld", svcname, ch_rc, ch_score);
}
/* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);