경고 : '쉽지 않은 여정이기 때문에 마음 단단히 먹어야 함'
brew install git
여기에서 커밋
, 풀
, 푸시
, 머지
등등 하면 됨 !
통신 규약을 지키지 않으면 요청을 하는 클라이언트
, 요청을 받는 서버
둘다 해당 메시지를 해석할 수 없음
프론트엔드(클라이언트): React
백엔드(서버):express
데이터베이스: mySql
활용API: 오픈뱅킹 API
CSS: styled-components
클라이언트: npm start
/ /3000
서버: node expressServer.js
/ /4000
npm install styled-components
npm install react-router-dom
npm install axios
npm install query-string
npm install qrcode.react
npm install react-qrcode-reader
npm install react-slick --force
npm install react-modal --force
npm install slick-carousel --force
npm install jsonwebtoken
npm install --save mysql2
npm install dotenv --save
npm install express
npm install axios
import React from "react";
import axios from "axios";
const AxiosComponent = () => {
const handleClick = () => {
axios.get("https://naver.com").then((res) => {
console.log(res);
});
};
return (
<div>
<button onClick={handleClick}>요청생성</button>
</div>
);
};
export default AxiosComponent;
import React from "react";
**import axios from "axios";**
const AxiosComponent = () => {
const handleClick = () => {
**axios
.get(
"https://newsapi.org/v2/everything?q=tesla&from=2023-08-13&sortBy=publishedAt&apiKey=개별apikey입력"
)
.then((res) => {
console.log(res);
});**
};
return (
<div>
<button onClick={handleClick}>요청생성</button>
</div>
);
};
export default AxiosComponent;
brew install --cask postman
최상위 컴포넌트 App
부모컴포넌트 NewsSearch
자식컴포넌트 HeaderComponent
, Search
, Result
<HeaderComponent />
<Search />
<Result />
import React, { useState } from "react";
import axios from "axios";
const Search = ({ onSearch }) => {
const [searchText, setSearchText] = useState("");
const handleClick = () => {
axios
.get(
`https://newsapi.org/v2/everything?q=${searchText}&from=2023-08-13&sortBy=publishedAt&apiKey=apikey입력&language=ko`
)
.then((res) => {
onSearch(res.data.articles);
});
};
const handleInputChange = (event) => {
setSearchText(event.target.value);
};
return (
<div>
<input type="text" value={searchText} onChange={handleInputChange} />
<button onClick={handleClick}>검색</button>
</div>
);
};
export default Search;
value
는 searchText
로 저장 상태 관리를 위해 useState사용import React from "react";
const Result = ({ articles }) => {
return (
<div>
<ul>
{articles.map((article, index) => (
<li key={index}>{article.title}</li>
))}
</ul>
</div>
);
};
export default Result;
const Search = ({ **onSearch** }) => {
}
axios
.get(
`https://newsapi.org/v2/everything?q=${searchText}&from=2023-08-13&sortBy=publishedAt&apiKey=apikey&language=ko`
)
.then((res) => {
**onSearch**(res.data.articles);
})
const [searchResults, setSearchResults] = useState([]);
// NewsSearch.jsx
<div>
<HeaderComponent />
<Search onSearch={handleSearch} />
<Result **articles**={searchResults} />
</div>
import React from "react";
const Result = ({ **articles** }) => {
return (
<div>
<ul>
{articles.map((article, index) => (
<li key={index}>{article.title}</li>
))}
</ul>
</div>
);
};
export default Result;
import React, { useState } from "react";
import HeaderComponent from "../components/HeaderComponent";
import SearchInputComponents from "../components/newsSearch/SearchInputComponents";
import NewsListComponents from "../components/newsSearch/NewsListComponents";
import axios from "axios";
const NewsSearch = () => {
const [searchValue, setSearchValue] = useState("");
const [newsList, setNewsList] = useState([]);
const handleChange = ({ target }) => {
const { value } = target;
console.log(value);
setSearchValue(value);
};
const handleClick = () => {
console.log("hello");
// axios 요청 작성하기
const apiKey = "";
axios
.get(
`https://newsapi.org/v2/everything?q=${searchValue}&from=2023-08-13&sortBy=publishedAt&apiKey=${apiKey}`
)
.then((response) => {
console.log(response);
setNewsList(response.data.articles);
});
};
return (
<div>
<HeaderComponent></HeaderComponent>
<SearchInputComponents
handleChange={handleChange}
handleClick={handleClick}
></SearchInputComponents>
<NewsListComponents newsList={newsList}></NewsListComponents>
</div>
);
};
export default NewsSearch;
import React from "react";
const SearchInputComponents = ({ handleChange, handleClick }) => {
return (
<div>
<div>
<input onChange={handleChange}></input>
<button onClick={handleClick}>전송</button>
</div>
</div>
);
};
export default SearchInputComponents;
import React from "react";
const NewsListComponents = ({ newsList }) => {
return (
<div>
{newsList.map((news) => {
return <>{news.title}</>;
})}
</div>
);
};
export default NewsListComponents;
이용중
으로 바꿈- 테스트API 호출 : https://testapi.openbanking.or.kr로 호출
- 오픈뱅킹에서 제공하는 인증을 사용
- 필수값이 Y인 항목만 이용
testapi
로 하기로 약속함~113
오류 메세지가 발생하면 성공
(시간만료, 이미 사용한 코드는 이런 에러가 발생)rsp_message": "인증요청거부-인증 파라미터 오류([3000113])"
다시 인증
을 해서 코드를 입력하면?정상적
으로 받아옴{
"access_token": "access_token",
"token_type": "Bearer",
"refresh_token": "refresh_token",
"expires_in": 7775999,
"scope": "inquiry login transfer",
"user_seq_no": "1101038125"
}
cf) 응답 메시지
명세
다시 send
를 해서 요청하면? 이미 사용한 code이기 때문에 다시 오류발생accesstoken
, 사용자 일련번호
필요
{
"api_tran_id": "api_tran_id",
"api_tran_dtm": "api_tran_dtm",
"rsp_code": "A0000",
"rsp_message": "",
"user_seq_no": "user_seq_no",
"user_ci": "0WSRNaalbJe40nXqYMeFMLas4YhCFMfIySdzGsSYRMz2v/qQQ6R1wvgEPirOt7FkUJiF1y9DUdsQvsjVRwKDIQ==",
"user_name": "송가영",
"res_cnt": "1",
"res_list": [
{
"fintech_use_num": "120230226588951223594984",
"account_alias": "오픈은행",
"bank_code_std": "097",
"bank_code_sub": "0000000",
"bank_name": "오픈은행",
"savings_bank_name": "",
"account_num_masked": "123412341***",
"account_seq": "",
"account_holder_name": "송가영",
"account_holder_type": "P",
"account_type": "1",
"inquiry_agree_yn": "Y",
"inquiry_agree_dtime": "20230913141953",
"transfer_agree_yn": "Y",
"transfer_agree_dtime": "20230913141953",
"payer_num": "20230913319841225825"
}
],
"inquiry_card_cnt": "0",
"inquiry_card_list": [],
"inquiry_pay_cnt": "0",
"inquiry_pay_list": [],
"inquiry_insurance_cnt": "0",
"inquiry_insurance_list": [],
"inquiry_loan_cnt": "0",
"inquiry_loan_list": []
}
#### 사용자인증 API
GET https://testapi.openbanking.or.kr/oauth/2.0/authorize
Parameter | Type |
---|---|
response_type | 고정값: code |
client_id | client_id |
redirect_uri | http://localhost:3000/authResult |
scope | login inquiry transfer |
state | 12345678901234567890123456789012 |
auth_type | 0 |
import React from "react";
import HeaderComponent from "../components/HeaderComponent";
const MainPage = () => {
const handleClick = () => {
// 새 창을 열기
const newWindow = window.open("", "_blank");
const clientId = process.env.REACT_APP_BANK_ID;
// 주소 설정
const authorizeUrl = `https://testapi.openbanking.or.kr/oauth/2.0/authorize?response_type=code&client_id=${clientId}&redirect_uri=http://localhost:3000/authResult&scope=login%20inquiry%20transfer&state=12345678901234567890123456789012&auth_type=0`;
// 새 창의 위치를 지정하지 않으면 기본적으로 중앙에 열립니다.
// 만약 위치를 지정하려면 다음과 같이 사용할 수 있습니다.
// const windowOptions = 'width=800,height=600,left=100,top=100';
// 새 창을 열고 주소로 이동
newWindow.location.href = authorizeUrl;
};
return (
<div>
<HeaderComponent title={"사용자 인증 센터 이동"}></HeaderComponent>
<button onClick={handleClick}>사용자 인증</button>
</div>
);
};
export default MainPage;
#### 사용자 토큰발급 API
POST https://testapi.openbanking.or.kr/oauth/2.0/token
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Parameter | Type |
---|---|
code | authorization_code |
client_id | client_id |
client_secret | client_secret |
redirect_uri | http://localhost:3000/authResult |
grant_type | 고정값: authorization_code |
쿼리 문자열로 파싱하기 위한 라이브러리 설치
npm install query-string
import React, { useState } from "react";
import HeaderComponent from "../components/HeaderComponent";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
import axios from "axios";
const AuthResult = () => {
const queryParams = useLocation().search;
const parsed = queryString.parse(queryParams);
const code = parsed.code;
const id = process.env.REACT_APP_BANK_ID;
const pw = process.env.REACT_APP_BANK_PW;
const [accessToken, setAccessToken] = useState("");
const [userSeqNo, setUserSeqNo] = useState("");
const handleClick = () => {
let requestOption = {
url: "/oauth/2.0/token",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
data: {
code: code,
client_id: id,
client_secret: pw,
redirect_uri: "http://localhost:3000/authResult",
grant_type: "authorization_code",
},
};
axios(requestOption).then(({ data }) => {
setAccessToken(data.access_token);
setUserSeqNo(data.user_seq_no);
if (data.rsp_code !== "O0001") {
localStorage.setItem("accessToken", data.access_token);
localStorage.setItem("userSeqNo", data.user_seq_no);
alert("저장 완료");
} else {
alert("인증에 실패했습니다 다시 시도해 주세요");
}
});
};
return (
<div>
<HeaderComponent title={"토큰 발급 / 인증"} />
<p>사용자 인증 코드 : {code}</p>
<button onClick={handleClick}>토큰 발급하기</button>
<p>accessToken : {accessToken}</p>
<p>userSeqNo : {userSeqNo}</p>
</div>
);
};
export default AuthResult;
queryString.parse(queryParams)
)하고 그결과를 parsed
변수에 저장useState
를 사용하여 accessToken
과 userSeqNo
상태 변수를 초기화handleClick
함수는 HTTP POST 요청을 사용하여 토큰을 요청하고 결과를 처리bank_trans_id
항목은 ‘이용기관코드+U+이용기관부여번호’로 이루어져 있는 것임import React, { useEffect, useState } from "react";
import MainAccountCard from "../components/list/MainAccountCard";
import axios from "axios";
import HeaderComponent from "../components/HeaderComponent";
const AccountList = () => {
const [accountList, setAccountList] = useState([]);
useEffect(() => {
console.log("data");
getAccountList();
}, []);
const getAccountList = () => {
let requestOption = {
url: "/v2.0/user/me",
method: "GET",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Authorization:
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMTAxMDM4MTI1Iiwic2NvcGUiOlsiaW5xdWlyeSIsImxvZ2luIiwidHJhbnNmZXIiXSwiaXNzIjoiaHR0cHM6Ly93d3cub3BlbmJhbmtpbmcub3Iua3IiLCJleHAiOjE3MDIzNjM1NjAsImp0aSI6ImMwYzc1YWM0LWU4MjctNDE3Yy1hZGI0LWY4ZWFjN2QwZjU2YyJ9.d7HKKhJiSJ0rDhViN7qUQdYt48ER_A73zrb6JnFimYU",
},
params: {
user_seq_no: "1101038125",
},
};
axios(requestOption).then((response) => {
console.log(response);
setAccountList(response.data.res_list);
});
};
return (
<div>
<HeaderComponent title={"계좌조회"}></HeaderComponent>
{accountList.map((account) => {
return (
<MainAccountCard
key={account.fintech_use_num}
bankName={account.bank_name}
fintechUseNo={account.fintech_use_num}
></MainAccountCard>
);
})}
</div>
);
};
export default AccountList;
이용기관코드
AccessToken
userSeqNo
import React, { useEffect, useState } from "react";
import axios from "axios";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
const BalanceList = () => {
const queryParams = useLocation().search;
const parsed = queryString.parse(queryParams);
const fintechNo = parsed.fintechUseNo;
const [balanceList, setBalanceList] = useState([]);
useEffect(() => {
getBalanceList();
}, []);
const getBalanceList = () => {
const accessToken = localStorage.getItem("accessToken");
let requestOption = {
url: "/v2.0/account/transaction_list/fin_num",
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
},
params: {
bank_tran_id: genTrasId(),
fintech_use_num: fintechNo,
inquiry_type: "A",
inquiry_base: "D",
from_date: "20201212",
to_date: "20230901",
sort_order: "D",
tran_dtime: "20230914103600",
},
};
axios(requestOption).then((response) => {
console.log(response);
setBalanceList(response.data.res_list);
});
};
function generateRandom9DigitNumber() {
const min = 100000000; // Minimum value (smallest 9-digit number)
const max = 999999999; // Maximum value (largest 9-digit number)
const random9DigitNumber =
Math.floor(Math.random() * (max - min + 1)) + min;
return random9DigitNumber.toString();
}
const genTrasId = () => {
return "M202300440U" + generateRandom9DigitNumber();
};
// Render the balanceList as a list
return (
<div>
<ul>
{balanceList.map((item, index) => (
<li key={index}>
순번: {index + 1}, 내용: {item.tran_type}, 거래금액: {item.tran_amt}
, 잔액: {item.after_balance_amt}
</li>
))}
</ul>
</div>
);
};
export default BalanceList;
npm install qrcode.react
import React from "react";
import HeaderComponent from "../components/HeaderComponent";
import { QRCodeSVG } from "qrcode.react";
import styled from "styled-components";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
const QrCode = () => {
const QRBlock = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin: 1rem;
`;
const queryParams = useLocation().search;
const parsed = queryString.parse(queryParams);
const fintechNo = parsed.fintechUseNo;
return (
<div>
<HeaderComponent title={"QR"}></HeaderComponent>
<QRBlock>
<QRCodeSVG size={256} value={fintechNo} />
<p>{fintechNo}</p>
</QRBlock>
</div>
);
};
export default QrCode;
npm i react-qrcode-reader
import React, { useState } from "react";
import HeaderComponent from "../components/HeaderComponent";
import QrCodeReader, { QRCode } from "react-qrcode-reader";
const QrReader = () => {
const [val, setVal] = useState("");
return (
<>
<HeaderComponent title={"QR Reader"} />
<QrCodeReader delay={100} width={600} height={500} action={setVal} />
<p>{val}</p>
</>
);
};
export default QrReader;
참고)
import axios from "axios";
import React, { useState } from "react";
import styled from "styled-components";
const ModalCardBlock = styled.div`
display: flex;
flex-direction: column;
margin: 0.5rem;
padding: 20px;
border: 1px #112211 solid;
`;
const CardTitle = styled.div`
font-size: 1rem;
color: black;
`;
const FintechUseNo = styled.div`
font-size: 0.7rem;
margin-bottom: 30px;
`;
const WithDrawButton = styled.button`
border: none;
padding: 0.3rem;
background: #2aa450;
color: white;
margin-top: 0.3rem;
`;
const ModalCard = ({ bankName, fintechUseNo, tofintechno }) => {
//fintechUseNo : 내계좌
//tofintechno : QR 코드로 읽어온 핀테크 계좌
const [amount, setamount] = useState("");
const [withdraw, setWithdraw] = useState([]);
const genTransId = () => {
let countnum = Math.floor(Math.random() * 1000000000) + 1;
const clientNo = "M202300440";
let transId = clientNo + "U" + countnum;
return transId;
};
const handlePayButtonClick = () => {
//출금이체 기능 작성
//rsp_code가 A000일때 alert("출금완료")가 발생하고 나머지는 오류 메세지를 띄우게
const accessToken = localStorage.getItem("accessToken");
let requestOption = {
url: "/v2.0/transfer/withdraw/fin_num",
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
Authorization: `Bearer ${accessToken}`,
},
body: {
bank_tran_id: genTransId,
cntr_account_type: "N",
cntr_account_num: "100000000001",
dps_print_content: "쇼핑몰환불",
fintech_use_num: fintechUseNo,
wd_print_content: "오픈뱅킹출금",
tran_amt: amount,
tran_dtime: "20230812130000",
req_client_name: "홍길동",
req_client_fintech_use_num: fintechUseNo,
req_client_num: "HONGGILDONG1234",
transfer_purpose: "ST",
recv_client_name: "유관우",
recv_client_bank_code: "097",
recv_client_account_num: "100000000001",
},
};
axios(requestOption)
.then((response) => {
console.log(response);
const { rsp_code } = response.data;
if (rsp_code === "A000") {
alert("이체성공");
} else {
alert("Error: 실패");
}
setWithdraw(response.data);
})
.catch((error) => {
console.error("Error:", error);
alert("Error: 실패");
});
};
const handleChange = (e) => {
const { value } = e.target;
console.log(value);
setamount(value);
};
return (
<ModalCardBlock>
<CardTitle>{bankName}</CardTitle>
<FintechUseNo>{fintechUseNo}</FintechUseNo>
<p>{tofintechno}로 돈을 보냅니다.</p>
<input onChange={handleChange}></input>
<WithDrawButton onClick={handlePayButtonClick}>결제하기</WithDrawButton>
</ModalCardBlock>
);
};
export default ModalCard;
bank_tran_id: genTransId()
A0000
if (rsp_code === "A000")
import axios from "axios";
import React, { useState } from "react";
import styled from "styled-components";
const ModalCardBlock = styled.div`
display: flex;
flex-direction: column;
margin: 0.5rem;
padding: 20px;
border: 1px #112211 solid;
`;
const CardTitle = styled.div`
font-size: 1rem;
color: black;
`;
const FintechUseNo = styled.div`
font-size: 0.7rem;
margin-bottom: 30px;
`;
const WithDrawButton = styled.button`
border: none;
padding: 0.3rem;
background: #2aa450;
color: white;
margin-top: 0.3rem;
`;
const ModalCard = ({ bankName, fintechUseNo, tofintechno }) => {
//fintechUseNo : 내계좌
//tofintechno : QR 코드로 읽어온 핀테크 계좌
const [amount, setamount] = useState("");
const [withdraw, setWithdraw] = useState([]);
const genTransId = () => {
let countnum = Math.floor(Math.random() * 1000000000) + 1;
const clientNo = "M202300440";
let transId = clientNo + "U" + countnum;
return transId;
};
const handlePayButtonClick = () => {
//출금이체 기능 작성
//rsp_code가 A000일때 alert("출금완료")가 발생하고 나머지는 오류 메세지를 띄우게
const accessToken = localStorage.getItem("accessToken");
let requestOption = {
url: "/v2.0/transfer/withdraw/fin_num",
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
Authorization: `Bearer ${accessToken}`,
},
data: {
bank_tran_id: genTransId(),
cntr_account_type: "N",
cntr_account_num: "100000000001",
dps_print_content: "쇼핑몰환불",
fintech_use_num: fintechUseNo,
wd_print_content: "오픈뱅킹출금",
tran_amt: amount,
tran_dtime: "20230812130000",
req_client_name: "홍길동",
req_client_fintech_use_num: fintechUseNo,
req_client_num: "HONGGILDONG1234",
transfer_purpose: "ST",
recv_client_name: "유관우",
recv_client_bank_code: "097",
recv_client_account_num: "100000000001",
},
};
axios(requestOption).then((response) => {
console.log(response);
const { rsp_code } = response.data;
**if (rsp_code === "A0000") {
alert("성공");
}**
setWithdraw(response.data);
});
};
const handleChange = (e) => {
const { value } = e.target;
console.log(value);
setamount(value);
};
return (
<ModalCardBlock>
<CardTitle>{bankName}</CardTitle>
<FintechUseNo>{fintechUseNo}</FintechUseNo>
<p>{tofintechno}로 돈을 보냅니다.</p>
<input onChange={handleChange}></input>
<WithDrawButton onClick={handlePayButtonClick}>결제하기</WithDrawButton>
</ModalCardBlock>
);
};
export default ModalCard;
Authorization
과 다름(scope
항목이 추가)fs
(파일 시스템)Crypto | Node.js v20.5.1 Documentation
const crypto = require("crypto");
const sha256Enc = (plainText, key) => {
const secret = key;
const hash = crypto
.createHmac("sha256", secret)
.update(plainText)
.digest("hex");
return hash;
};
암호화 복호화
npm install jsonwebtoken
brew install --cask mysqlworkbench
open -a MySQLWorkbench
npm install --save mysql2
cf) .env
관리 라이브러리 적용
npm install dotenv --save
const mysql = require("mysql2");
**const dotenv = require("dotenv");
dotenv.config();**
// create the connection to database
const connection = mysql.createConnection({
**host: process.env.DB_HOST,
user: process.env.DB_ACCOUNT,
password: process.env.DB_PASSWORD,**
database: "fintech",
});
// simple query
connection.query("쿼리자리", function (err, results, fields) {
console.log(err);
console.log(results); // results contains rows returned by server
console.log(fields); // fields contains extra meta data about results, if available
});
세부설정
npm install express
const express = require("express");
const dotenv = require("dotenv");
const app = express();
dotenv.config();
app.get("/", function (req, res) {
res.send("Hello World");
});
app.listen(process.env.PORT);
const express = require("express");
const dotenv = require("dotenv");
const app = express();
dotenv.config();
app.get("/", function (req, res) {
res.send("Hello World");
});
//라우터추가
app.post("/", (req, res) => {
console.log(req);
res.send("hello");
});
app.listen(process.env.PORT);
로그인 기능
const express = require("express");
const dotenv = require("dotenv");
const mysql = require("mysql2");
var jwt = require("jsonwebtoken");
const app = express();
dotenv.config();
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_ACCOUNT,
password: process.env.DB_PASSWORD,
database: "fintech",
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get("/", function (req, res) {
res.send("Hello World");
});
app.post("/", (req, res) => {
console.log(req.body);
res.send("hello");
});
app.post("/login", (req, res) => {
const { userAccount, password } = req.body;
const sql =
"SELECT user_id, user_account, user_password FROM fintech.user WHERE user_account = ?";
connection.query(sql, [userAccount], (err, result) => {
if (err) throw err;
console.log(result);
if (password === result[0].user_password) {
let tokenKey = "f@i#n%tne#ckfhlafkd0102test!@#%";
jwt.sign(
{
userId: result[0].user_id,
userEmail: result[0].user_account,
},
tokenKey,
{
expiresIn: "10d",
issuer: "fintech.admin",
subject: "user.login.info",
},
function (err, token) {
if (err) {
console.error(err);
}
console.log("로그인 성공", token);
res.json(token);
}
);
}
});
});
app.listen("4001");
const express = require("express");
const dotenv = require("dotenv");
const mysql = require("mysql2");
var jwt = require("jsonwebtoken");
**const crypto = require("crypto");**
const app = express();
dotenv.config();
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_ACCOUNT,
password: process.env.DB_PASSWORD,
database: "fintech",
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get("/", function (req, res) {
res.send("Hello World");
});
app.post("/", (req, res) => {
console.log(req.body);
res.send("hello");
});
app.post("/login", (req, res) => {
const { userAccount, password } = req.body;
const sql =
"SELECT user_id, user_account, user_password FROM fintech.user WHERE user_account = ?";
connection.query(sql, [userAccount], (err, result) => {
if (err) throw err;
console.log(result);
**let encPassword = sha256Enc(password, "fintech");**
if (encPassword === result[0].user_password) {
let tokenKey = "f@i#n%tne#ckfhlafkd0102test!@#%";
jwt.sign(
{
userId: result[0].user_id,
userEmail: result[0].user_account,
},
tokenKey,
{
expiresIn: "10d",
issuer: "fintech.admin",
subject: "user.login.info",
},
function (err, token) {
if (err) {
console.error(err);
}
console.log("로그인 성공", token);
res.json(token);
}
);
} else {
res.json("비밀번호 다릅니다.");
}
});
});
**const sha256Enc = (plainText, key) => {
const secret = key;
const hash = crypto
.createHmac("sha256", secret)
.update(plainText)
.digest("base64");
return hash;
};**
app.listen("4001");
AccountList.jsx
const getAccountList = () => {
**const ourtoken = localStorage.getItem("ourtoken");**
let requestOption = {
url: "/account",
method: "GET",
headers: {
**ourtoken: ourtoken,**
},
};
axios(requestOption).then((response) => {
console.log(response);
setAccountList(response.data.res_list);
});
};
app.get("/authResult", (req, res) => {
const authCode = req.query.code;
let requestOption = {
url: "https://testapi.openbanking.or.kr/oauth/2.0/token",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
data: {
code: authCode,
client_id: process.env.FINTECH_CLIENT_ID,
client_secret: process.env.FINTECH_CLIENT_SECRET,
redirect_uri: "http://localhost:4000/authResult",
grant_type: "authorization_code",
},
};
axios(requestOption).then(({ data }) => {
if (data.rsp_code !== "O0001") {
console.log(data);
} else {
console.log(data);
}
});
});
/authResult
authCode
를 받아서 토큰을 발급authCode
를 이용해서 엔드포인트로 POST
요청을 보냄POST 요청
let requestOption = {
url: "/oauth/2.0/token",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
data: {
code: code,
client_id: id,
client_secret: pw,
redirect_uri: "http://localhost:3000/authResult",
grant_type: "authorization_code",
},
};
axios(requestOption).then(({ data }) => {
setAccessToken(data.access_token);
setUserSeqNo(data.user_seq_no);
if (data.rsp_code !== "O0001") {
localStorage.setItem("accessToken", data.access_token);
localStorage.setItem("userSeqNo", data.user_seq_no);
alert("저장 완료");
} else {
alert("인증에 실패했습니다 다시 시도해 주세요");
}
});
app.get("/account", auth, (req, res) => {
let { userId } = req.decoded;
console.log(req.decoded);
const sql = "SELECT * FROM user WHERE user_id = ?";
connection.query(sql, [userId], function (err, result) {
console.log(result);
const accesstoken = result[0].access_token;
const userSeqNo = result[0].user_seq_no;
console.log(accesstoken);
const sendData = {
user_seq_no: userSeqNo,
};
const option = {
method: "GET",
url: "https://testapi.openbanking.or.kr/v2.0/user/me",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Authorization: `Bearer ${accesstoken}`,
},
params: sendData,
};
axios(option).then(({ data }) => {
res.json(data);
});
});
});
/account
auth
- 인증되지 않은 사용자일 경우 엔트포인트에 접근 불가auth
를 통과한(인증된 사용자일 경우) req.decoded
(사용자 정보가 디코딩되어 해당 객체에 저장되어 있음)를 userId
에 넣음user_id
가 userId
와 일치하는 사용자 정보를 조회result
에 저장access_token
(사용자 인증토근)과 user_seq_no
(사용자 일련번호)를 추출option
객체에 구성/v2.0/user/me
엔드포인트에서 사용자 정보를 조회headers
에는 요청 헤더를 설정하며, 여기서는 Authorization
헤더에 사용자의 access_token
을 포함시켜 인증params
객체에는 요청 파라미터로 사용자의 user_seq_no
를 전달const ourtoken = localStorage.getItem("ourtoken");
let requestOption = {
url: "/account",
method: "GET",
headers: {
ourtoken: ourtoken,
},
};
axios(requestOption).then((response) => {
console.log(response);
setAccountList(response.data.res_list);
});
GET https://testapi.openbanking.or.kr/oauth/2.0/authorize
Parameter | Type | Description |
---|---|---|
response_type | 고정값: code | Required. OAuth 2.0 인증 요청 시 반환되는 형태 |
client_id | <client_id> | Required. 오픈뱅킹에서 발급한 이용기관 앱의 Client ID |
redirect_uri | http://localhost:3000/authResult | Required. 사용자인증이 성공하면 이용기관으로 연결되는 URL |
scope | login inquiry transfer | Required. Access Token 권한 범위 |
state | 12345678901234567890123456789012 | Required. CSRF 보안위협에 대응하기 위해 이용기관이 세팅하는 난수값 |
auth_type | 0 | Required. (0:최초인증, 1:재인증, 2:인증생략) |
POST https://testapi.openbanking.or.kr/oauth/2.0/token
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Parameter | Type | Description |
---|---|---|
code | <authorization_code> | Required. 사용자인증 성공 후 획득한 Authorization Code |
client_id | <client_id> | Required. 오픈뱅킹에서 발급한 이용기관 앱의 Client ID |
client_secret | <client_secret> | Required. 오픈뱅킹에서 발급한 이용기관 앱의 Client Secret |
redirect_uri | http://localhost:3000/authResult | Required. Access Token 을 전달받을 Callback URL |
grant_type | 고정값: authorization_code | Required. 3-legged 인증을 위한 권한부여 방식 지정 |
GET https://testapi.openbanking.or.kr/v2.0/user/me
Header | Type | Description |
---|---|---|
Authorization | Bearer <access_token> | Required. 오픈뱅킹으로부터 전송받은 Access Token 을 HTTP Header 에 추가 |
Parameter | Type | Description |
---|---|---|
user_seq_no | 고정값: code | Required. 사용자일련번호, 토큰 발급받은 후 응답메세지에 포함 |
GET https://testapi.openbanking.or.kr/v2.0/account/balance/fin_num
Header | Type | Description |
---|---|---|
Authorization | Bearer <access_token> | Required. 오픈뱅킹으로부터 전송받은 Access Token 을 HTTP Header 에 추가 |
Parameter | Type | Description |
---|---|---|
bank_tran_id | 거래고유번호 | Required. 이용기관코드+U+이용기관부여번호 |
fintech_use_num | 고정값: code | Required. 핀테크이용번호 |
tran_dtime | 20230914101010 | Required. 요청일시 |
GET https://testapi.openbanking.or.kr/v2.0/account/transaction_list/fin_num
Header | Type | Description |
---|---|---|
Authorization | Bearer <access_token> | Required. 오픈뱅킹으로부터 전송받은 Access Token 을 HTTP Header 에 추가 |
Parameter | Type | Description |
---|---|---|
bank_tran_id | 거래고유번호 | Required. 이용기관코드+U+이용기관부여번호 |
fintech_use_num | 고정값: code | Required. 핀테크이용번호 |
inquiry_type | A | Required. 조회구분코드 - “A”:All, “I”:입금, “O”:출금 |
inquiry_base | D | Required. 조회기준코드 - “D”:일자, “T”:시간 |
from_date | 20201212 | Required. 조회시작일자 |
to_date | 20230901 | Required. 조회종료일자 |
sort_order | D | Required. 정렬순서 - “D”:Descending, “A”:Ascending |
tran_dtime | 20230914101010 | Required. 요청일시 |
POST https://openapi.openbanking.or.kr/v2.0/transfer/withdraw/fin_num
Content-Type: application/json; charset=UTF-8
Header | Type | Description |
---|---|---|
Authorization | Bearer <access_token> | Required. 오픈뱅킹으로부터 전송받은 Access Token 을 HTTP Header 에 추가 |
Parameter | Type | Description |
---|---|---|
bank_tran_id | 거래고유번호 | Required. 거래고유번호 |
cntr_account_type | N | Required. 약정 계좌/계정 구분 - “N”:계좌, “C”:계정 |
cntr_account_num | 100000000001 | Required. 약정 계좌/계정 번호 |
dps_print_content | 쇼핑몰환불 | Required. 입금계좌인자내역 |
fintech_use_num | fintech_use_num | Required. 출금계좌핀테크이용번호 |
wd_print_content | 오픈뱅킹출금 | Required. 오픈뱅킹에서 발급한 이용기관 앱의 Client Secret |
tran_amt | 1000 | Required. 거래금액 |
tran_dtime | 고정값: authorization_code | Required. 요청일시 |
req_client_name | 홍길동 | Required. 요청고객성명 |
req_client_fintech_use_num | req_client_fintech_use_num | 요청고객핀테크이용번호 |
req_client_num | HONGGILDONG1234 | Required. 요청고객회원번호 |
transfer_purpose | ST | Required. 이체용도 |
recv_client_name | 유관우 | 최종수취고객성명 |
recv_client_bank_code | 097 | 최종수취고객계좌 개설기관.표준코드 |
recv_client_account_num | 100000000001 | 최종수취고객계좌번호 |
POST https://testapi.openbanking.or.kr/v2.0/transfer/deposit/fin_num
Content-Type: application/json; charset=UTF-8
Header | Type | Description |
---|---|---|
Authorization | Bearer <access_token> | Required. 오픈뱅킹으로부터 전송받은 Access Token 을 HTTP Header 에 추가 |
Parameter | Type | Description |
---|---|---|
cntr_account_type | N | Required. 약정 계좌/계정 구분 - “N”:계좌, “C”:계정 |
cntr_account_num | 200000000001 | Required. 약정 계좌/계정 번호 |
wd_pass_phrase | NONE | Required. 입금이체용 암호문구 |
wd_print_content | 환불금액 | Required. 출금계좌인자내역 |
name_check_option | off | Required. 수취인성명 검증 여부 - “on”:검증함, “off”:미검증 (미지정 시 기본값: "on") |
tran_dtime | 20230812130000 | Required. 요청일시 |
req_cnt | 1 | Required. 입금요청건수 - 입금요청건수는 1 건만 신청이 가능함. |
req_list | `` | Required. 입금요청목록 |
--tran_no | 1 | Required. 거래순번 |
--bank_tran_id | bank_tran_id | Required. 거래고유번호 |
--fintech_use_num | fintech_use_num | Required. 핀테크이용번호 |
--print_content | 오픈서비스캐시백 | Required. 입금계좌인자내역 |
--tran_amt | 1000 | Required. 거래금액 |
--req_client_name | 홍길동 | Required. 요청고객성명 |
--req_client_fintech_use_num | req_client_fintech_use_num | 요청고객핀테크이용번호주 |
--req_client_num | HONGGILDONG1234 | Required. 요청고객회원번호 |
--transfer_purpose | ST | Required. 이체용도 |
POST https://testapi.openbanking.or.kr/oauth/2.0/token
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Parameter | Type | Description |
---|---|---|
client_id | client_id | Required. 거래고유번호 |
client_secret | client_secret | Required. 약정 계좌/계정 구분 - “N”:계좌, “C”:계정 |
scope | 고정값 oob | Required. Access token 권한 범위 |
grant_type | 고정값 client_credentials | Required. 2-legged 인증을 위한 권한부여 방식 지정 |
안녕하세요 좋은글 감사합니다.
오픈뱅킹 API 사용하여 실제 본인 계좌나 카드내역 조회는 안되는것인가요? test-bed 내에서만 활용 가능한지 궁금합니다.